<?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>In the box</title>
	<atom:link href="http://inthebox.webmin.com/feed" rel="self" type="application/rss+xml" />
	<link>http://inthebox.webmin.com</link>
	<description>Automating the data center</description>
	<lastBuildDate>Sat, 24 Oct 2009 23:16:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Preparing Your Enterprise for Cloud Computing at Processor&#160;Magazine</title>
		<link>http://inthebox.webmin.com/preparing-your-enterprise-for-cloud-computing-at-processor-magazine</link>
		<comments>http://inthebox.webmin.com/preparing-your-enterprise-for-cloud-computing-at-processor-magazine#comments</comments>
		<pubDate>Sat, 24 Oct 2009 23:16:26 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Snippets]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=101</guid>
		<description><![CDATA[Carmi Levy wrote a nice article over at Processor Magazine about preparing your enterprise for cloud computing, and I was one of the folks in the industry he talked to. It&#8217;s a good introductory piece for companies thinking about moving into the cloud.]]></description>
			<content:encoded><![CDATA[<p>Carmi Levy wrote a <a href="http://bit.ly/2XgGMy">nice article over at Processor Magazine</a> about preparing your enterprise for cloud computing, and I was one of the folks in the industry he talked to.  It&#8217;s a good introductory piece for companies thinking about moving into the cloud.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/preparing-your-enterprise-for-cloud-computing-at-processor-magazine/feed</wfw:commentRss>
		<slash:comments>-1</slash:comments>
		</item>
		<item>
		<title>Multi-account Twittering from the command&#160;line</title>
		<link>http://inthebox.webmin.com/multi-account-twittering-from-the-command-line</link>
		<comments>http://inthebox.webmin.com/multi-account-twittering-from-the-command-line#comments</comments>
		<pubDate>Wed, 10 Jun 2009 03:54:58 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Snippets]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=86</guid>
		<description><![CDATA[While working on our migration from Joomla to Drupal over at Virtualmin.com, I&#8217;ve been keeping folks apprised of what I&#8217;m up to by posting the highlights to Twitter on our virtualmin Twitter account. So, I&#8217;ve had to switch back and forth quite a bit from my personal account. It also means I have to have [...]]]></description>
			<content:encoded><![CDATA[<p>While working on our migration from Joomla to Drupal over at Virtualmin.com, I&#8217;ve been keeping folks apprised of what I&#8217;m up to by posting the highlights to Twitter on our <a href="http://twitter.com/virtualmin">virtualmin</a> Twitter account.  So, I&#8217;ve had to switch back and forth quite a bit from my personal account.  It also means I have to have another tab open quite frequently for Twitter.  I looked around for a standalone Twitter client that supports multiple accounts, and found the pickings were pretty slim on Linux (a few Air clients run on Linux, but not very well, unfortunately).  But, I got to thinking that a command line client would be ideal&#8230;so I searched the web, and found a brief article <a href="http://anojrs.blogspot.com/2008/03/twittering-from-command-line.html">here</a> with a simple one-liner to submit a tweet to Twitter.  The fact that Twitter works with a one-line shell script is awesome, and explains why there are so many Twitter clients.</p>
<p>Anyway, I first thought, &#8220;I could just make copies of that for each account.&#8221;  The I could call, &#8220;twitter-swelljoe&#8221; for my personal account and &#8220;twitter-virtualmin&#8221; for my business account.  But that seemed wrong, somehow.</p>
<p>So, I wrote a version that accepts a command line argument of the username, and then sends the rest as a tweet.</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/bin/bash</span>
<span style="color: #007800;">USER</span>=$<span style="color: #000000;">1</span>
<span style="color: #7a0874; font-weight: bold;">shift</span>
<span style="color: #007800;">TWIT</span>=$<span style="color: #000000; font-weight: bold;">@</span>
<span style="color: #007800;">PASS</span>=password
curl <span style="color: #660033;">-u</span> <span style="color: #007800;">$USER</span>:<span style="color: #007800;">$PASS</span> <span style="color: #660033;">-d</span> <span style="color: #007800;">status</span>=<span style="color: #ff0000;">&quot;$TWIT&quot;</span> http:<span style="color: #000000; font-weight: bold;">//</span>twitter.com<span style="color: #000000; font-weight: bold;">/</span>statuses<span style="color: #000000; font-weight: bold;">/</span>update.xml<span style="color: #000000; font-weight: bold;">&gt;/</span>dev<span style="color: #000000; font-weight: bold;">/</span>null</pre></div></div>

<p>Nothing really fancy going on here, but the &#8220;shift&#8221; is saying, &#8220;drop the first token from the arguments list&#8221;, and then $@ says &#8220;give me the rest&#8221;.  So, no quotes required for the tweet.  I saved the filed as tw, and use it like so:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">tw swelljoe Tweeting from the <span style="color: #7a0874; font-weight: bold;">command</span> line<span style="color: #000000; font-weight: bold;">!</span>
tw virtualmin Business-y kinds of tweets from the <span style="color: #7a0874; font-weight: bold;">command</span> line.</pre></div></div>

<p>This assumes one password for all Twitter accounts, which is exactly what I do anyway (I use <a href="http://supergenpass.com">SuperGenPass</a>, which always generates the same password when given the same URL and passphrase).  You could instead store the passwords in an associative array (gotta be using Bash 4.0+ for that), something like this (I can&#8217;t test this, as I don&#8217;t have bash 4.0!):</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/bin/bash</span>
<span style="color: #007800;">USER</span>=$<span style="color: #000000;">1</span>
<span style="color: #7a0874; font-weight: bold;">shift</span>
<span style="color: #007800;">TWIT</span>=$<span style="color: #000000; font-weight: bold;">@</span>
&nbsp;
<span style="color: #7a0874; font-weight: bold;">declare</span> <span style="color: #660033;">-A</span> PASS
PASS<span style="color: #7a0874; font-weight: bold;">&#91;</span>swelljoe<span style="color: #7a0874; font-weight: bold;">&#93;</span>=<span style="color: #ff0000;">&quot;passwd&quot;</span>
PASS<span style="color: #7a0874; font-weight: bold;">&#91;</span>virtualmin<span style="color: #7a0874; font-weight: bold;">&#93;</span>=<span style="color: #ff0000;">&quot;notapasswd&quot;</span>
&nbsp;
curl <span style="color: #660033;">-u</span> <span style="color: #007800;">$USER</span>:<span style="color: #800000;">${PASS[$USER]}</span> <span style="color: #660033;">-d</span> <span style="color: #007800;">status</span>=<span style="color: #ff0000;">&quot;$TWIT&quot;</span> http:<span style="color: #000000; font-weight: bold;">//</span>twitter.com<span style="color: #000000; font-weight: bold;">/</span>statuses<span style="color: #000000; font-weight: bold;">/</span>update.xml<span style="color: #000000; font-weight: bold;">&gt;/</span>dev<span style="color: #000000; font-weight: bold;">/</span>null</pre></div></div>

<p>There are ways to fake associative arrays in older versions of bash, or use other tools, but since I don&#8217;t need multiple passwords, I&#8217;m going to just hand-wave that away.</p>
<p>I find it amusing that the first version of this twitter client, at 137 characters, is a valid tweet.  It makes me want to go golfing and see how small I can make an even more functional version&#8230;at the very least, I can&#8217;t resist:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">tw swelljoe <span style="color: #000000; font-weight: bold;">`</span><span style="color: #c20cb9; font-weight: bold;">cat</span> tw<span style="color: #000000; font-weight: bold;">`</span></pre></div></div>

<p>And then I have to go change my password after realizing I tweeted the real version rather than the demonstration version.  Good thing nobody follows my personal tweets.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/multi-account-twittering-from-the-command-line/feed</wfw:commentRss>
		<slash:comments>-1</slash:comments>
		</item>
		<item>
		<title>First impressions of the&#160;G1</title>
		<link>http://inthebox.webmin.com/first-impressions-of-the-g1</link>
		<comments>http://inthebox.webmin.com/first-impressions-of-the-g1#comments</comments>
		<pubDate>Thu, 23 Oct 2008 00:55:09 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[Features]]></category>
		<category><![CDATA[Mobile]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=75</guid>
		<description><![CDATA[Someone over on Hacker News asked me what I thought about the new G1 (aka &#8220;Google phone&#8221;), as compared to the iPhone (which Jamie wrote about here).  I figured those comments would be generally useful to folks thinking about picking between the two coolest phones available right now.  I&#8217;ve now had a full day to [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/images/G1-inthebox.JPG"></img></p>
<p>Someone over on <a href="http://news.ycombinator.com">Hacker News</a> asked me what I thought about the new G1 (aka &#8220;Google phone&#8221;), as compared to the iPhone (which Jamie wrote about <a href="/creating-an-iphone-ui-for-virtualmin-part-1">here</a>).  I figured those comments would be generally useful to folks thinking about picking between the two coolest phones available right now.  I&#8217;ve now had a full day to play with it, and I&#8217;ve spent several days tinkering with iPhones, so here&#8217;s my off-the-cuff &#8220;review&#8221; of the new G1.</p>
<p><span style="color: #000000;">The hardware is mildly disappointing. Not as nice &#8220;feeling&#8221; as the iPhone (though the 3G iPhone also now has a plastic back and doesn&#8217;t feel as nice as the first-gen iPhone, so I guess things are tough all over), or even as solid feeling as my old Sidekick. When taking the back off to put in the battery and SIM card, I felt like it was going to break. It didn&#8217;t, but it felt like it was. Likewise for the little covers for the data/charge port and the SD memory slot&#8230;they&#8217;re plastic and tiny and feel fragile. The one exception is the keypad, which feels very nice to me.  But, keeping in mind that the G1 is dramatically cheaper than the iPhone 3G ($179 up front and cheaper per month for the plan&#8211;though I haven&#8217;t actually looked at different plans, I just kept the one I&#8217;ve been on, which is about $10 cheaper than iPhone&#8211;so $240 over 2 years), I think it&#8217;s a great buy, and a whole lot of hardware for a very small price.  I think I probably would have been happy to pay $20 more for slightly sturdier construction, though, particularly on the port cover&#8230;I have a bad feeling that it&#8217;s going to get broken long before I&#8217;m ready to upgrade to a new phone.<br />
</span></p>
<p><span style="color: #000000;">Price wasn&#8217;t my primary deciding factor (openness was), but it certainly didn&#8217;t hurt that I wouldn&#8217;t have to pay more per month to upgrade to the G1, while I&#8217;d pay out $240 more over 2 years for the iPhone 3G for the same service. I&#8217;m going to see if I can drop down to a smaller voice plan&#8211;the Sidekick I had before had minimum plan requirements, but I didn&#8217;t see any such requirements when signing up for the G1, so I might even be able to <em>save</em> $5 or $10 per month with the G1 over the Sidekick, since I rarely use the phone. I use maybe 100 minutes per month of a 1000 minute plan.</span><span style="color: #000000;"><br />
</span></p>
<p><span class="comment"><span style="color: #000000;">Software-wise, it&#8217;s plain awesome. The lack of two-finger gestures, as found on the iPhone, is somewhat disappointing, but it&#8217;s no slower to use the popup magnifier buttons, once you&#8217;re accustomed to it. Dragging and such is smooth and accurate (seeimngly more accurate than the iPhone, for me, but maybe it just feels that way because the keypad means that I don&#8217;t have to use it for typing fiddly stuff, as on the iPhone), so I guess the touch screen is pretty good quality.</span><span style="color: #000000;"> Since the Android developers are some of the same folks that developed the Sidekick, everything feels very intuitive to me (where, as usual, &#8220;intuitive&#8221; means, &#8220;what I&#8217;m used to&#8221;). It also has Linux underneath everything, so that may also be a &#8220;comfort&#8221; factor for me, I&#8217;m not sure.</span></p>
<p><span style="color: #000000;">Basic phone features work well and are easy to use, it sounds good and clear, and having Google mail, contacts, calendar, etc. is <em>sweet</em> (my old phone couldn&#8217;t handle more than about 500 messages via IMAP, and I get more messages than that in a week, and obviously GMail just works great with practically infinite mail). Web pages look great, and browsing is fast. WiFi was quick and easy to setup. YouTube videos work great, both on 3G and on WiFi. It&#8217;s my understanding that T-Mobile&#8217;s 3G network is still somewhat small&#8230;so if you don&#8217;t live in the valley, your mileage may vary, but it works fine for me here in Mountain View.</span></p>
<p><img title="g1-ssh2" src="/images/G1-ssh2.JPG" alt="" width="496" height="456" /></p>
<p><span style="color: #000000;">I installed an Open Source ssh client off of the web called ConnectBot; no jail breaking required, which was a big issue for me with the iPhone. I don&#8217;t want to have to have permission to install arbitrary apps that I&#8217;ve written or someone else has written. I also installed Compare Anywhere, and a bubble game from the app store, and the quality of everything is really slick. Really impressive for a launch day catalog, especially since everything is free right now. I haven&#8217;t spent a lot of time with the apps on the iPhone, so I don&#8217;t have much to compare to. But, I&#8217;m excited to play with more stuff from the catalog, and I think I&#8217;m going to try my hand at writing an app or two for the platform.<br />
</span></p>
<p><span style="color: #000000;">Also, it worked right away when I plugged it into my Linux desktop. No futzing around with weird stuff to get music onto the device. The iPhone/iPod is a <em>bitch</em> in that regard. That one thing made me ecstatic in ways I haven&#8217;t felt over a device in a long time. Coming off of years of messing around with iPods and an iPhone, and it never quite working right, having a drag and drop music experience is miraculous. Take this with a grain of salt, as I may be strange. I find iTunes incredibly confusing and difficult to use, so on those occasions when I&#8217;ve given up on getting Linux to work and rebooted into Windows, or borrowed a friends Macbook, and run iTunes for the purpose of putting music onto the device, I&#8217;ve ended up spending a long time futzing around <em>anyway</em>, because I never could figure out what all the syncing options meant or how to use them&#8230;so, every time I would fiddle until something happened, and occasionally it would just end up deleting all of my songs either on the PC or the device, and I would give up in disgust. I also have trouble using several other Apple software products, and find them hard to use, so I could just be too stupid to be trusted with a computer without some adult supervision.</span></p>
<p><span style="color: #000000;">I think it will be interesting to see how this battle plays out in the market, and I&#8217;m certain that the G1 is just the opening salvo.  Even if the G1 &#8220;loses&#8221; the battle against iPhone (if selling out all available units even before the launch date can really be considered a &#8220;loss&#8221; in anyone&#8217;s book), and fails to pickup significant market share, there will be another battle in a few months, and the battles will come faster and more furiously as other manufacturers adopt Android.  And each one will wear Apple down, and will open a new front on which Apple will either have to engage or ignore.  For example, very low end phones will come into existence next year that provide smart phone capabilities, as will higher end devices with more capabilities or special purpose capabilities to answer niche markets.  Apple can&#8217;t fight on all fronts, and every niche it loses strengthens the value of the Android platform to developers, and thus to end users.  If Android sucked, like Windows Mobile, this wouldn&#8217;t be a cause for concern for the folks at Apple.  But Android doesn&#8217;t suck.  It&#8217;s really nice to use.  As nice as iPhone?  Maybe not&#8230;but getting better rapidly (if you tried the last developer release a few months ago, you&#8217;ll know that there have been a lot of improvements since then).</span></p>
<p><span style="color: #000000;">As with PC vs. Mac two decades ago, it&#8217;s a battle of two different ideologies.  On one side, you have openness: the ability for hardware makers to produce widely varying hardware while still providing the same basic user interface; and on the other, you have a single source eco-system: Apple designs every aspect of its products and can control every element of the user experience, down to the very applications that run on the platform.  Except, this time around, there are two additional elements: the telcos, and Open Source.  While the telcos are going to fight to keep all cell phones locked down pretty tightly, and try to insure that they can extract money for just about everything novel you want to do with it, the Open Source nature of Android is going to allow people to do things with mobile devices that have been impossible to date (even on very powerful, but locked down, devices like the iPhone).</span></p>
<p><span style="color: #000000;">To me, it looks like Apple is going to make the same mistakes they made with the Mac years ago: Treating their application developers poorly by competing with them unfairly or simply locking them out of the market, disrespecting users that want to use their devices in ways not imagined by Jobs and treating them not as customers but enemies, and finally, denial that price is a major factor in peoples purchasing decisions.  If it plays out as it did in the PC wars, Apple will find that they have fewer applications for their devices, and a steadily declining market share (even as actual sales continue to increase, since the smart phone market is growing rapidly, and all ships rise in a growing market).  Since it isn&#8217;t Apple vs. Microsoft, this time, but instead Apple vs. Open Source (and openness in general), I know which side I&#8217;m on.  I&#8217;ve had a tendency to pull for the scrappy underdog in the past, and Apple has very frequently been the scrappy underdog&#8230;but unfortunately it&#8217;s an underdog with a Napoleon complex, so it&#8217;s not exactly a good choice for replacing the old tyrant.  But, luckily, this time around, we have a wide open alternative&#8230;and it&#8217;s actually really good and really easy to use.  Maybe the &#8220;Open Source on the desktop&#8221; movement, to date, has all been preparation for this moment&#8230;the moment when Open Source can <em>finally</em> be a great option for regular, non-technical, consumers.  I think that&#8217;s pretty exciting.  And we&#8217;ll just have to wait and see if Apple winds up on the wrong side of history, again.</p>
<p></span></p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/first-impressions-of-the-g1/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>A couple of nice articles about Webmin and&#160;Virtualmin</title>
		<link>http://inthebox.webmin.com/a-couple-of-nice-articles-about-webmin-and-virtualmin</link>
		<comments>http://inthebox.webmin.com/a-couple-of-nice-articles-about-webmin-and-virtualmin#comments</comments>
		<pubDate>Wed, 03 Sep 2008 22:58:03 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Snippets]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=62</guid>
		<description><![CDATA[I use Google&#8217;s awesome Alerts feature to keep an ear out for anything interesting on the web about some of my favorite topics.  Obviously, Webmin and Virtualmin are among those topics. A great article about Webmin showed up today at Free Software Magazine, by Gary Richmond, that is a &#8220;bird&#8217;s eye view&#8221; of the subject, [...]]]></description>
			<content:encoded><![CDATA[<p>I use Google&#8217;s awesome <a href="http://www.google.com/alerts">Alerts</a> feature to keep an ear out for anything interesting on the web about some of my favorite topics.  Obviously, Webmin and Virtualmin are among those topics.</p>
<p>A <a href="http://www.freesoftwaremagazine.com/columns/webmin_can_graphical_front_end_system_administration_replace_command_line">great article about Webmin</a> showed up today at <a href="http://www.freesoftwaremagazine.com">Free Software Magazine</a>, by Gary Richmond, that is a &#8220;bird&#8217;s eye view&#8221; of the subject, just touching on the high points.  In it, he gets to the heart of some of the things that make Webmin great, like it&#8217;s ability to edit configuration files directly and safely, rather than generating them from templates, as most similar configuration tools do.  All in all, a nice little &#8220;boss-friendly, while still being accurate&#8221; introduction to Webmin.</p>
<p>And Monday, the awesome blog <a href="http://thenextweb.org">The Next Web</a> covered Virtualmin, the business and the project, kicking off their new series on profitable web businesses (rather than those that are running on dreams and VC cash) with this article: <em><a href="http://thenextweb.org/2008/09/01/ep1-companies-who-make-money-virtualmin/">Companies who make money: Virtualmin</a>.</em> Lots of great stuff over there.</p>
<p>And, of course, no discussion of the blogosphere would be complete without mentioning the new browser from Google.  I haven&#8217;t tried it yet, as it&#8217;s only available for Windows and I&#8217;m a Linux user when I&#8217;m working, but Jamie tells me that Webmin works just fine (expected) and there was only one quirky bit in the Virtualmin theme (surprising, since we test against Konqueror and Safari, both WebKit browsers, but easily fixed).  So, if you&#8217;re running Windows, give it a go: <a href="http://www.google.com/chrome">Chrome</a>.  If you&#8217;re an IE user, <i>please</i> give it a go.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/a-couple-of-nice-articles-about-webmin-and-virtualmin/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Creating an iPhone UI for&#160;Virtualmin</title>
		<link>http://inthebox.webmin.com/creating-an-iphone-ui-for-virtualmin-part-1</link>
		<comments>http://inthebox.webmin.com/creating-an-iphone-ui-for-virtualmin-part-1#comments</comments>
		<pubDate>Fri, 22 Aug 2008 20:15:59 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Features]]></category>
		<category><![CDATA[virtualmin webmin iphone iui mobile]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=33</guid>
		<description><![CDATA[Introduction and History Around the start of the year, I created a theme for Webmin designed to make it easier to access from mobile devices like smartphones and cellphones with simple built-in browsers. At the time I had a Treo 650, which had a very basic browser &#8211; certainly not powerful enough to render the [...]]]></description>
			<content:encoded><![CDATA[<h2>Introduction and History</h2>
<p>Around the start of the year, I created a theme for Webmin designed to make it easier to access from mobile devices like smartphones and cellphones with simple built-in browsers. At the time I had a Treo 650, which had a very basic browser &#8211; certainly not powerful enough to render the standard Virtualmin or Webmin framed themes.</p>
<p>By using Webmin&#8217;s theming features, I was able to create a UI that used multiple levels of menus to access global and domain-level settings, instead of frames. The theme also changed the layouts of forms to be more vertical than horizontal, use fewer tables, and remove all use of Javascript and CSS for hiding sections.</p>
<p>This was released in the virtual-server-mobile theme package version 1.6, and all was good in the world. Anyone using it could now access all the features of Virtualmin from a very basic browser, and read mail in Usermin without having to rely on the awful IMAP implementations in most smartphones.</p>
<p>This shows what Virtualmin looked like on a Treo :</p>
<p><img class="alignnone" title="Virtualmin domain links" src="http://www.webmin.com/images/treo_edit.png" alt="" width="322" height="323" /></p>
<p>Then I bought an iPhone.</p>
<p>It has a <strong>much</strong> more capable browser, technically the equal of any desktop browser like Firefox or IE. The regular Webmin themes actually worked fine, although a fair bit of zooming is needed to use them. The mobile theme looked like crap, as it didn&#8217;t use any of the browser features like CSS and Javascript that the iPhone supports. Plus the layout rendered poorly due to the use of long lines of text that didn&#8217;t get wrapped at the browser&#8217;s screen width.</p>
<p>On the iPhone, the Create Alias page in mobile theme looked like this :</p>
<p><a href="http://inthebox.webmin.com/wp-content/uploads/2008/08/iphone_mobile.jpg"><img class="alignnone size-full wp-image-42" title="Old mobile theme on iPhone" src="http://inthebox.webmin.com/wp-content/uploads/2008/08/iphone_mobile.jpg" alt="" width="320" height="480" /></a></p>
<p>And in the regular Virtualmin theme, the Create Alias page looked like :</p>
<p><a href="http://inthebox.webmin.com/wp-content/uploads/2008/08/iphone_framed.jpg"><img class="alignnone size-full wp-image-43" title="Framed theme on iPhone" src="http://inthebox.webmin.com/wp-content/uploads/2008/08/iphone_framed.jpg" alt="" width="320" height="480" /></a></p>
<p>I mentioned this to Joe, and he pointed me at <a href="http://code.google.com/p/iui/">iUI</a>, an awesome library of CSS and Javascript that allows developers to create websites that mimic the look of native iPhone applications. After trying out the demos and looking at their source code, it was clear that iUI would be perfect for creating an iPhone-specific theme.</p>
<p>It wasn&#8217;t quite as simple as I first thought, but after some hacking on both the theme code and iUI itself I was able to come up with a pretty good layout, as you can see in this screenshot of the Create Alias page :</p>
<p><img class="alignnone" title="Virtualmin alias form on iPhone" src="http://www.webmin.com/images/mobile_alias.png" alt="" width="320" height="480" /></p>
<h2>Menu Implementation</h2>
<p>Actually getting IUI to play nicely with the Webmin theming system was slightly more complex than I originally expected though. For example, an iPhone-style multi-level menu that slides to the left is implemented in IUI with HTML like :</p>
<pre>&lt;ul id='main' title='My Menu' selected='true'&gt;
&lt;li&gt;&lt;a href='#menu1'&gt;Submenu One&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href='#menu2'&gt;Submenu Two&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id='menu1' title='Submenu One'&gt;
&lt;li&gt;&lt;a href='foo.cgi'&gt;Foo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href='bar.cgi'&gt;Bar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id='menu2' title='Submenu Two'&gt;
&lt;li&gt;&lt;a href='quux.cgi'&gt;Quux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href='#page'&gt;Some page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div id='page' class='panel' title='Some page'&gt;
Any HTML can go here.
&lt;/div&gt;</pre>
<p>As you might guess, CSS and Javascript are used to show only one menu or div at a time, even though they are all in the same HTML file. This is quite different to the way menus are usually created in Webmin.</p>
<p>To get this kind of HTML from the theme, I created an index.cgi that generates a large set of &lt;ul&gt; lists and &lt;div&gt; blocks containing all the Virtualmin domains, global settings, Webmin categories and modules. This is loaded by the iPhone when a user logs in, and allows quick navigation to without any additional page loads. For example, these screenshots show the path down to the Users and Groups module. Only the last requires an extra page load :</p>
<p><a href="http://inthebox.webmin.com/wp-content/uploads/2008/08/menu1.jpg"><img class="alignnone size-medium wp-image-48" title="Top Level Menu" src="http://inthebox.webmin.com/wp-content/uploads/2008/08/menu1-200x300.jpg" alt="" width="200" height="300" /></a><a href="http://inthebox.webmin.com/wp-content/uploads/2008/08/menu2.jpg"><img class="alignnone size-medium wp-image-49" title="Webmin Categories Menu" src="http://inthebox.webmin.com/wp-content/uploads/2008/08/menu2-200x300.jpg" alt="" width="200" height="300" /></a><a href="http://inthebox.webmin.com/wp-content/uploads/2008/08/menu3.jpg"><img class="alignnone size-medium wp-image-50" title="Webmin Modules" src="http://inthebox.webmin.com/wp-content/uploads/2008/08/menu3-200x300.jpg" alt="" width="200" height="300" /></a><a href="http://inthebox.webmin.com/wp-content/uploads/2008/08/menu4.jpg"><img class="alignnone size-medium wp-image-51" title="Users and Groups" src="http://inthebox.webmin.com/wp-content/uploads/2008/08/menu4-200x300.jpg" alt="" width="200" height="300" /></a></p>
<p>The index.cgi script is able to fetch all Webmin modules and categories with the functions <strong>get_visible_module_infos</strong> and <strong>list_categories</strong>, which are part of the core API. It also fetches Virtualmin domains with <strong>virtual_server::list_domains</strong> and global actions with <strong>virtual_server::get_all_global_links</strong>.</p>
<p>For example, the code that generates the menus of modules and categories looks roughly like :</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@modules</span> <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>get_visible_module_infos<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">%cats</span> <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>list_categories<span style="color: #009900;">&#40;</span>\<span style="color: #0000ff;">@modules</span><span style="color: #009900;">&#41;</span>;
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;&lt;ul id='modules' title='Webmin Modules'&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
<span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$c</span> <span style="color: #009900;">&#40;</span><span style="color: #000066;">sort</span> <span style="color: #009900;">&#123;</span> <span style="color: #0000ff;">$b</span> <span style="color: #b1b100;">cmp</span> <span style="color: #0000ff;">$a</span> <span style="color: #009900;">&#125;</span> <span style="color: #009900;">&#40;</span><span style="color: #000066;">keys</span> <span style="color: #0000ff;">%cats</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;&lt;li&gt;&lt;a href='#cat_$c'&gt;$cats{$c}&lt;/a&gt;&lt;/li&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
    <span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$c</span> <span style="color: #009900;">&#40;</span><span style="color: #000066;">sort</span> <span style="color: #009900;">&#123;</span> <span style="color: #0000ff;">$b</span> <span style="color: #b1b100;">cmp</span> <span style="color: #0000ff;">$a</span> <span style="color: #009900;">&#125;</span> <span style="color: #009900;">&#40;</span><span style="color: #000066;">keys</span> <span style="color: #0000ff;">%cats</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@incat</span> <span style="color: #339933;">=</span> <span style="color: #000066;">grep</span> <span style="color: #009900;">&#123;</span> <span style="color: #0000ff;">$_</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#123;</span><span style="">'category'</span><span style="color: #009900;">&#125;</span> eq <span style="color: #0000ff;">$c</span> <span style="color: #009900;">&#125;</span> <span style="color: #0000ff;">@modules</span>;
    <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;&lt;ul id='cat_$c' title='$cats{$c}'&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
    <span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$m</span> <span style="color: #009900;">&#40;</span><span style="color: #000066;">sort</span> <span style="color: #009900;">&#123;</span> <span style="color: #000066;">lc</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$a</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#123;</span><span style="">'desc'</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">cmp</span> <span style="color: #000066;">lc</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$b</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#123;</span><span style="">'desc'</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#125;</span> <span style="color: #0000ff;">@incat</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;&lt;li&gt;&lt;a href='$m-&gt;{'dir'}/' target=_self&gt;$m-&gt;{'desc'}&lt;/a&gt;&lt;/li&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
        <span style="color: #009900;">&#125;</span>
    <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;&lt;/ul&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
    <span style="color: #009900;">&#125;</span>
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;&lt;/ul&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;</pre></div></div>

<p>The actual IUI styling and menu navigation comes from CSS and Javascript files which are referenced on every page in the &lt;head&gt; section, generated by the theme&#8217;s <strong>theme_header</strong> function which overrides the Webmin <strong>header</strong> call.</p>
<h2>Other Pages</h2>
<p>Other pages within Webmin are generated using the regular CGI scripts, but with their HTML modified by the theme. This is done by overriding many of the <strong>ui_</strong> family of functions, in particular those that generate forms with labels and input fields. Because the iPhone screen is relatively narrow, it is more suited to a layout in which all labels and inputs are arranged vertically, rather than the Webmin default that uses multiple columns.</p>
<p>For example, the <strong>theme_ui_table_row</strong> override function contains code like :</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$label</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">/\S/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #0000ff;">$rv</span> .<span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot;&lt;div class='webminTableName'&gt;$label&lt;/div&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
    <span style="color: #009900;">&#125;</span>
<span style="color: #0000ff;">$rv</span> .<span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot;&lt;div class='webminTableValue'&gt;$value&lt;/div&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;</pre></div></div>

<p>The label and value variables are the field label and input HTML respectively. The actual styling is done using CSS classes that were added to IUI for the theme. The same thing is done in functions that render multi-column tabs, tabs and other input elements generated with <strong>ui_</strong> family functions.</p>
<p>The only downside to this approach is that not all Webmin modules have yet been converted to use the functions in <strong>ui-lib.pl</strong>, and so do not get the iPhone-style theming. However, I am working on a long-term project to convert all modules from manually generated HTML to the using the UI library functions.</p>
<h2>Headers and Footers</h2>
<p>In most Webmin themes, there are links at the bottom of each page back to previous pages in the heirarchy &#8211; for example, when editing a Unix group there is a link back to the list of all groups.</p>
<p>However, IUI puts the back link at the top of the page next to the title, as in native iPhone applications. Fortunately, CSS absolute positioning allows the theme to place this link at the top, even though it is only generated at the end of the HTML. The generated HTML for this looks like :</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;div class='toolbar'&gt;
&lt;h1 id='pageTitle'&gt;&lt;/h1&gt;
&lt;a class='button indexButton' href='/useradmin/index.cgi?mode=groups' target=_self&gt;Back&lt;/a&gt;
&lt;a class='button' href='/help.cgi/useradmin/edit_group'&gt;Help&lt;/a&gt;
&lt;/div&gt;</pre></div></div>

<p>The <strong>toolbar</strong> CSS class contains the magic attributes needed to position it at the top of the page, even though the theme outputs it last.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/creating-an-iphone-ui-for-virtualmin-part-1/feed</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Disqus&#160;comments</title>
		<link>http://inthebox.webmin.com/disqus-comments</link>
		<comments>http://inthebox.webmin.com/disqus-comments#comments</comments>
		<pubDate>Mon, 18 Aug 2008 23:01:56 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[Snippets]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=30</guid>
		<description><![CDATA[I&#8217;ve just converted In the box over to the Disqus comment network, which provides a really nice comments UI, good spam filtering, and a discussion board. And, with the new version of their WordPress API it integrates seamlessly. It also allows anyone with a Disqus account to comment without signing up for yet another blog. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just converted <em>In the box</em> over to the <a href="http://disqus.com">Disqus</a> comment network, which provides a really nice comments UI, good spam filtering, and a discussion board.  And, with the new version of their WordPress API it integrates seamlessly.  It also allows anyone with a Disqus account to comment without signing up for yet another blog.  It&#8217;s a cool service, and makes time spent commenting on blogs more effective.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/disqus-comments/feed</wfw:commentRss>
		<slash:comments>67</slash:comments>
		</item>
		<item>
		<title>Old School to New School: Refactoring Perl (part&#160;2)</title>
		<link>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl-part-2</link>
		<comments>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl-part-2#comments</comments>
		<pubDate>Sun, 03 Aug 2008 00:20:39 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Features]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=28</guid>
		<description><![CDATA[When I left off, I&#8217;d made an old chunk of Perl code (that mostly pre-dates widespread availability of Perl 5) warnings and strict compliant, converted it to be easily usable as both a module and a command line script, added some POD documentation, and built a couple of rudimentary tests to make it possible to [...]]]></description>
			<content:encoded><![CDATA[<p>When I left off, I&#8217;d made an old chunk of Perl code (that mostly pre-dates widespread availability of Perl 5) warnings and strict compliant, converted it to be easily usable as both a module and a command line script, added some POD documentation, and built a couple of rudimentary tests to make it possible to change the code without fearing breakage.  Now we can get rough with it.</p>
<p><strong>Refactoring for Clarity and Brevity</strong></p>
<p>Despite the changes, so far, the code is pretty much as it was when we started.  A little more verbose due to the changes for strict compliance, so that&#8217;s a negative, and the addition of a <em>main</em> function and the <em>oschooser</em> wrapper just adds even more lines of code.  The main code block was already a little bit long for comfort at several pages worth of 80 column text, assuming a 50 row editor window.  Jamie seems capable of holding a lot of code in his head at once&#8230;me, I&#8217;m kinda slow, so I like small digestible chunks.  So, let&#8217;s start digesting.</p>
<p>Looking through the code, this bit jumped right out.  It&#8217;s a little bit unwieldy:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;">    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$auto</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;"># Failed .. give up</span>
      <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Failed to detect operating system<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
      <span style="color: #000066;">exit</span> <span style="color: #cc66cc;">1</span>;
      <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">elsif</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$auto</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">3</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;"># Do we have a tty?</span>
      <span style="color: #000066;">local</span> <span style="color: #0000ff;">$rv</span> <span style="color: #339933;">=</span> <span style="color: #000066;">system</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;tty &gt;/dev/null 2&gt;&amp;1&quot;</span><span style="color: #009900;">&#41;</span>;
      <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$?</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Failed to detect operating system<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
        <span style="color: #000066;">exit</span> <span style="color: #cc66cc;">1</span>;
        <span style="color: #009900;">&#125;</span>
      <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #0000ff;">$auto</span> <span style="color: #339933;">=</span> 0;
        <span style="color: #009900;">&#125;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;"># Ask the user</span>
      <span style="color: #0000ff;">$auto</span> <span style="color: #339933;">=</span> 0;
      <span style="color: #009900;">&#125;</span></pre></div></div>

<p>It seemed like this could be shortened a little bit by making a <em>have_tty</em> function, and using <em>&#038;&#038;</em> in the <em>elsif</em>.  Not a huge difference, but if we then flip the tests over (there&#8217;s only four possible values of <em>$auto</em> and they only result in two possible outcomes) and add an <em>||</em> we can lose a few more lines, one conditional, and cut it down to:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;">    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$auto</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">3</span> <span style="color: #339933;">&amp;&amp;</span> have_tty<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> || <span style="color: #0000ff;">$auto</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #0000ff;">$auto</span> <span style="color: #339933;">=</span> 0;
      <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;"># Failed .. give up</span>
      <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Failed to detect operating system<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
      <span style="color: #000066;">exit</span> <span style="color: #cc66cc;">1</span>;
      <span style="color: #009900;">&#125;</span></pre></div></div>

<p>That&#8217;s a lot less code to read.  I also think it makes more sense to have a single failure block and a single block setting <em>$auto</em>.  I&#8217;m still trying to figure out the purpose of <em>auto=2</em>, since it seems like it would only be possible to fall back to asking a question, if there&#8217;s actually a TTY.  But Jamie knows the quirks of various systems far better than I do, so we&#8217;ll leave it alone, for now, and keep the same behavior.  I bet it&#8217;s to accommodate something funny on Windows!</p>
<p>I&#8217;ve also made a few tweaks, during this process, adding a <em>package OsChooser;</em> statement to the beginning of the file.  I also discovered that <em>$uname</em> actually <em>is</em> being used in this code!  It&#8217;s hidden inside the <em>os_list.txt</em> definitions.  There are a couple of <em>eval</em> statements being used to execute arbitrary code on each line found in the OS list.  Jamie must have been a Lisp hacker in a former life, with all this willy nilly mixing code and data.  This is a pretty clever bit of code, but it took me a little while to grok, but now that I know what&#8217;s going on, we can solve some of the problems we had with testing detection of systems other than the one we&#8217;re running on.</p>
<p>In the meantime, just know that <em>$uname</em> has returned as an <em>our</em> variable, so that it can be &#8220;global&#8221; without ticking off strict.</p>
<p><strong>More Tests</strong></p>
<p>So, automated testing of an OS detection program isn&#8217;t a whole lot of good, if I can only test detection of one operating system (the one it happens to be running on right now).  So, we need to introduce a bit more flexibility in where the OS-related data comes from.  This is trickier than it sounds.  UNIX and Linux has never standardized on one single location for identifying the OS.  Many systems identify themselves in <em>/etc/issue</em>, while those from Red Hat use <em>/etc/redhat-release</em> (they also have a reasonable issue file, but I&#8217;m guessing it&#8217;s not reliably present or reliably consistent in its contents, as Jamie has chosen to use the release file, instead), and Debian has a <em>/etc/debian_version</em> file.  Sun Solaris and the BSD-based systems seem to all use <em>uname</em>, and that&#8217;s just the popular ones.  Webmin also supports a few dozen more branches of the UNIX tree, plus most modern Windows versions!</p>
<p>So, looking at <em>oschooser.pl</em> you&#8217;re probably wondering where the heck all of that extra stuff happens, because it doesn&#8217;t really have any detection code of its own.  The answer is in <em>os_list.txt</em>, which is a file with lines like the following:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;">Fedora Linux     <span style="color: #ff0000;">&quot;Fedora $1&quot;</span> fedora  <span style="color: #0000ff;">$1</span>    <span style="color: #ff0000;">`cat /etc/fedora-release 2&gt;/dev/null`</span> <span style="color: #339933;">=~</span> 
<span style="color: #009966; font-style: italic;">/Fedora.*\s([0-9\.]+)\s/i</span> || <span style="color: #ff0000;">`cat /etc/fedora-release 2&gt;/dev/null`</span> <span style="color: #339933;">=~</span> <span style="color: #339933;">/</span>Fedora.<span style="color: #339933;">*</span>\sFC<span style="color: #009900;">&#40;</span>\S<span style="color: #339933;">+</span><span style="color: #009900;">&#41;</span>\<span style="color: #000066;">s</span><span style="color: #339933;">/</span>i</pre></div></div>

<p>This is a tab-delimited file.  Why tabs? I have no idea, and it&#8217;s been a source of errors for me several times&#8230;even Jamie isn&#8217;t sure why he chose tabs as the delimiter, but that&#8217;s the way it is.  It is plain text, plus numbered match variables, plus an optional snippet of Perl that will be executed via <em>eval</em> if it exists.  This makes for an extremely flexible and powerful tool, if a wee bit intimidating on first glimpse.</p>
<p>So, that last field is the tricky bit.  The thing I&#8217;m going to have to contend with if I want to be able to test every OS that Webmin supports, rather than just the one that happens to be sitting under the code while the tests are running.  I&#8217;ll need a new argument to our <em>oschooser</em> function for starters, called <em>$issue</em>, which will generically contain whatever it is that <em>os_list.txt</em> uses to recognize a particular OS.  On my Fedora 7 desktop system, that&#8217;s <em>/etc/redhat-release</em>, which contains:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">Fedora release <span style="color: #000000;">7</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>Moonshine<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre></div></div>

<p>So, <em>oschooser</em> now contains:</p>
<pre lang"perl">
sub oschooser {
my ($oslist, $out, $auto, $issue) = @_;
...
}
</pre>
<p>Next, we need to make sure we keep the provided <em>$issue</em> if we got it, so we change this:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;">  <span style="color: #666666; font-style: italic;"># Try to guess the OS name and version</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">-</span>r <span style="color: #ff0000;">&quot;/etc/.issue&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #0000ff;">$etc_issue</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`cat /etc/.issue`</span>;
    <span style="color: #009900;">&#125;</span>
  <span style="color: #b1b100;">elsif</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">-</span>r <span style="color: #ff0000;">&quot;/etc/issue&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #0000ff;">$etc_issue</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`cat /etc/issue`</span>;
    <span style="color: #009900;">&#125;</span>
  <span style="color: #0000ff;">$uname</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`uname -a`</span>;</pre></div></div>

<p>Into:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># Try to guess the OS name and version</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$etc_issue</span>;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$issue</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #0000ff;">$etc_issue</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`cat $issue`</span>;
  <span style="color: #0000ff;">$uname</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$etc_issue</span>; <span style="color: #666666; font-style: italic;"># Strangely, I think this will work fine.</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">elsif</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">-</span>r <span style="color: #ff0000;">&quot;/etc/.issue&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #0000ff;">$etc_issue</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`cat /etc/.issue`</span>;
  <span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">elsif</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">-</span>r <span style="color: #ff0000;">&quot;/etc/issue&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #0000ff;">$etc_issue</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`cat /etc/issue`</span>;
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>Note that <em>$uname</em> is defined earlier in the code now&#8230;and merely gets over-written if we&#8217;ve set the <em>$issue</em> variable in our function call.</p>
<p>And then we have to do something about the contents of the last field in <em>os_list.txt</em> before it gets <em>eval</em>uated.  This is where it gets a little hairy.  In the <em>foreach</em> that iterates through each line in the file testing whether we have a match or not, I&#8217;ve added a new first condition, so it now looks like:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$o</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">@list</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$issue</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #0000ff;">$o</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">4</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #0000ff;">$o</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">4</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=~</span> <span style="color: #000066;">s</span><span style="color: #666666; font-style: italic;">#cat [/a-zA-Z\-]*#cat $issue#g;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #666666; font-style: italic;"># Testable, but this regex substitution is dumb.XXX</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$o</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">4</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #000066;">eval</span> <span style="color: #ff0000;">&quot;$o-&gt;[4]&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #666666; font-style: italic;"># Got a match! Resolve the versions</span>
    <span style="color: #0000ff;">$ver_ref</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$o</span>;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$ver_ref</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">/\$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #0000ff;">$ver_ref</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000066;">eval</span> <span style="color: #ff0000;">&quot;($o-&gt;[4]); $ver_ref-&gt;[1]&quot;</span>;
      <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$ver_ref</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">3</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">/\$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #0000ff;">$ver_ref</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">3</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000066;">eval</span> <span style="color: #ff0000;">&quot;($o-&gt;[4]); $ver_ref-&gt;[3]&quot;</span>;
      <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">last</span>;
    <span style="color: #009900;">&#125;</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$@</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000066;">print</span> <span style="color: #000000; font-weight: bold;">STDERR</span> <span style="color: #ff0000;">&quot;Error parsing $o-&gt;[4]<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span>
  <span style="color: #000066;">return</span> <span style="color: #0000ff;">$ver_ref</span>;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Which performs a substitution on the last field, if it contains a <em>cat</em> command.  It replaces it with the issue file that we&#8217;ve provided in the <em>$issue</em> variable.  Thus, we can now pass in <em>t/fedora-7.issue</em> and put a copy of the <em>/etc/redhat-release</em> file mentioned above, and we&#8217;ll be able to test detection of Fedora 7, no matter what operating system the test is actually running on.  I suspect we may run into trouble when we expand our <em>os_list.txt</em> to the full Webmin list, since I&#8217;m working with just the limited subset of systems the Virtualmin installer supports (or that I might support in the next year or so).  I&#8217;ve made a comment in the code with <em>XXX</em> (merely a convention used in the Webmin codebase, though any odd sequence of characters that you&#8217;ll remember works fine&#8230;many folks use <em>FIXME</em>) to remind myself of this suspicion later if I do run into problems that this is the first place I ought to look.</p>
<p>After these changes, it&#8217;s possible to get serious about testing.  So, I&#8217;ve added tests for a couple dozen systems, which was more Googling than coding due to the data-driven nature of my tests, and confirmed the new code is behaving identically to the old.  Which means it&#8217;s time for&#8230;</p>
<p><strong>More Refactoring</strong></p>
<p>If you&#8217;ve been following along, you know that <em>oschooser</em> is still awfully long.  A good tactic in such situations is to look for bits of functionality that can be pushed down into their own subroutines.  One good choice is the parsing of patterns file at the very beginning of the function:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@list</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@names</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">%donename</span>;
<span style="color: #000066;">open</span><span style="color: #009900;">&#40;</span>OS<span style="color: #339933;">,</span> <span style="color: #0000ff;">$oslist</span><span style="color: #009900;">&#41;</span> || <span style="color: #000066;">die</span> <span style="color: #ff0000;">&quot;failed to open $oslist : $!&quot;</span>;
<span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span><span style="color: #009999;">&lt;OS&gt;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000066;">chop</span>;
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/^([^\t]+)\t+([^\t]+)\t+([^\t]+)\t+([^\t]+)\t*(.*)$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000066;">push</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">@list</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#91;</span> <span style="color: #0000ff;">$1</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$2</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$3</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$4</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$5</span> <span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>;
    <span style="color: #000066;">push</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">@names</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$1</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #0000ff;">$donename</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$1</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>;
    <span style="color: #0000ff;">$names_to_real</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$1</span><span style="color: #009900;">&#125;</span> ||= <span style="color: #0000ff;">$3</span>;
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #000066;">close</span><span style="color: #009900;">&#40;</span>OS<span style="color: #009900;">&#41;</span>;</pre></div></div>

<p>This is a good place to start, because it only depends on one variable from outside the work, <em>$oslist</em>, which is the name of the OS definitions file.  And, of course, file access is always a good candidate for abstraction&#8230;what if, some day, we want to pull these definitions from a database or a __DATA__ section?  Having it all in one obvious location might be a win.  For now, I just want that bloody long <em>oschooser</em> function to be a little bit shorter, so we&#8217;ll create this parse_patterns function:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">sub</span> parse_patterns<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #b1b100;">my</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$oslist</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">@_</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@list</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@names</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">%donename</span>;
<span style="color: #666666; font-style: italic;"># Parse the patterns file</span>
<span style="color: #000066;">open</span><span style="color: #009900;">&#40;</span>OS<span style="color: #339933;">,</span> <span style="color: #0000ff;">$oslist</span><span style="color: #009900;">&#41;</span> || <span style="color: #000066;">die</span> <span style="color: #ff0000;">&quot;failed to open $oslist : $!&quot;</span>;
<span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span><span style="color: #009999;">&lt;OS&gt;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000066;">chop</span>;
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009966; font-style: italic;">/^([^\t]+)\t+([^\t]+)\t+([^\t]+)\t+([^\t]+)\t*(.*)$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000066;">push</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">@list</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#91;</span> <span style="color: #0000ff;">$1</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$2</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$3</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$4</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$5</span> <span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>;
    <span style="color: #000066;">push</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">@names</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$1</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #0000ff;">$donename</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$1</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>;
    <span style="color: #0000ff;">$NAMES_TO_REAL</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$1</span><span style="color: #009900;">&#125;</span> ||= <span style="color: #0000ff;">$3</span>;
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #000066;">close</span><span style="color: #009900;">&#40;</span>OS<span style="color: #009900;">&#41;</span>;
<span style="color: #000066;">return</span> <span style="color: #009900;">&#40;</span>\<span style="color: #0000ff;">@list</span><span style="color: #339933;">,</span> \<span style="color: #0000ff;">@names</span><span style="color: #009900;">&#41;</span>;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>That&#8217;s not too bad, and it shaves about 13 lines off of <em>oschooser</em> at a cost of 3 or 4 more lines of function baggage in the whole file.  The biggest irritant might be that I&#8217;m now passing around two array refs (one of which is already an array reference, so now we&#8217;ve got a reference to an array of references).  I get confused when I use too many references, because I&#8217;m addle-brained that way, but these are only a little bit nested and so not <em>too</em> complicated, so I think future readers of the code should be fine.  At least, no worse than they were before I got ahold of this script.</p>
<p>I&#8217;ve also converted <em>%names_to_real</em> to <em>%NAMES_TO_REAL</em> as it has become a package scoped global variable, and it&#8217;s considered good form to warn folks when they&#8217;ve come upon a global by shouting at them.  Of course, I have another global, <em>$uname</em>, which I haven&#8217;t renamed to all caps, as one of my mandates for myself on this project is to require no changes to the Webmin <em>os_list.txt</em>.  As I write this, I&#8217;m beginning to have second thoughts about <em>$uname</em> needing to be a global&#8230;so we&#8217;ll come back to that later.</p>
<p>Capturing the results of <em>parse_patterns</em> and dumping them out into <em>@name</em> and <em>@list</em> lets us run our tests again.</p>
<p><strong>And More Refactoring Still</strong></p>
<p>Things have improved a little in <em>oschooser</em>.  It almost fits into two screenfuls on my 20&#8243; monitor.  But I think we can do better.  I&#8217;m aiming for one page or less per function, in this exercise, so we&#8217;ve gotta keep moving.  The next distinct piece of functionality I see is the automatic OS detection code, so I&#8217;ll add a new <em>auto_detect</em> function, something like this:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">sub</span> auto_detect<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #b1b100;">my</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$oslist</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$issue</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$list_ref</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">@_</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$ver_ref</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@list</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">@$list_ref</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$uname</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`uname -a`</span>;
&nbsp;
<span style="color: #666666; font-style: italic;"># Try to guess the OS name and version</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$etc_issue</span>;
&nbsp;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$issue</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #0000ff;">$etc_issue</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`cat $issue`</span>;
  <span style="color: #0000ff;">$uname</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$etc_issue</span>; <span style="color: #666666; font-style: italic;"># Strangely, I think this will work fine.</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">elsif</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">-</span>r <span style="color: #ff0000;">&quot;/etc/.issue&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #0000ff;">$etc_issue</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`cat /etc/.issue`</span>;
  <span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">elsif</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">-</span>r <span style="color: #ff0000;">&quot;/etc/issue&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #0000ff;">$etc_issue</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">`cat /etc/issue`</span>;
  <span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$o</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">@list</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$issue</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #0000ff;">$o</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">4</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #0000ff;">$o</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">4</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=~</span> <span style="color: #000066;">s</span><span style="color: #666666; font-style: italic;">#cat [/a-zA-Z\-]*#cat $issue#g;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #666666; font-style: italic;"># Testable, but this regex substitution is dumb.XXX</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$o</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">4</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #000066;">eval</span> <span style="color: #ff0000;">&quot;$o-&gt;[4]&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #666666; font-style: italic;"># Got a match! Resolve the versions</span>
    <span style="color: #0000ff;">$ver_ref</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$o</span>;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$ver_ref</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">/\$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #0000ff;">$ver_ref</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000066;">eval</span> <span style="color: #ff0000;">&quot;($o-&gt;[4]); $ver_ref-&gt;[1]&quot;</span>;
      <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$ver_ref</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">3</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">/\$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #0000ff;">$ver_ref</span><span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">3</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000066;">eval</span> <span style="color: #ff0000;">&quot;($o-&gt;[4]); $ver_ref-&gt;[3]&quot;</span>;
      <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">last</span>;
    <span style="color: #009900;">&#125;</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$@</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000066;">print</span> <span style="color: #000000; font-weight: bold;">STDERR</span> <span style="color: #ff0000;">&quot;Error parsing $o-&gt;[4]<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span>
  <span style="color: #000066;">return</span> <span style="color: #0000ff;">$ver_ref</span>;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>You may note that I did rethink the globalization of <em>$uname</em> and found that it fit comfortably into this block, so I&#8217;ve killed a global introduced earlier in this process.  Now I&#8217;ve not even sure why I thought I needed it somewhere else, which is a nice thing about refactoring: You realize how little you understood what was going on when you first looked at the code.  Here&#8217;s also where we make use of the <em>@list</em> built during <em>parse_patterns</em>, and I dereference it before using it, though that&#8217;s probably more verbosity than needed.  I could also access it directly within the ref with, <em>@{$list_ref}</em>.</p>
<p>Finally, I&#8217;m returning <em>$ver_ref</em>, which contains a reference to an array of fields that describes the operating system detected.  Now that I&#8217;ve split this out, I realize that this OS version array could quite easily be mapped into a hash and turned into an object rather trivially, but that&#8217;s an exercise for another day.  For now, I just want to feel confident that I&#8217;ve made a functionally identical clone of <em>oschooser.pl</em> that I can use and extend painlessly and without fear of breakage.  So, let&#8217;s keep going.</p>
<p><strong>A Few More New Functions, and Killing Unused Code Softly</strong></p>
<p>As with <em>auto_detect</em> there is a big chunk of code that is used specifically for asking the user to choose the operating system and version from a list of options.  This is triggered in the following cases: <em>$auto</em> is set to 0 or any other false value, <em>$auto</em> is not false but auto-detection failed and one of the non-exit auto options is chosen and viable.  So, we can easily break out this whole bunch of functionality into its own function, called <em>ask_user</em>.  Like <em>auto_detect</em>, it requires the <em>$list_ref</em> array reference, and it also needs the <em>$names_ref</em>, since it will be interacting with the end user and they&#8217;ll be more comfortable seeing the &#8220;real names&#8221; of the available operating systems.  Also like <em>auto_detect</em>, it returns a <em>$ver_ref</em> which points to the array containing the full description of the OS.</p>
<p>When I got to this function, I noticed a huge block of unused code, which provides support for the <em>dialog</em> command on systems that support it (mostly just Red Hat based Linux distributions).  <em>dialog</em> is a simple tool for adding attractive ncurses interfaces to shell scripts.  I&#8217;m not sure why the code is being skipped with an <em>if (0)</em> statement, but I have only two choices for what to do about it, if my goal is to simplify this script and make it more robust: Enable it and fix whatever problems it has, possibly making it into its own reusable and independently testable function; or, simply remove the code altogether.  Webmin and the installer libraries for Virtualmin are both in SVN.  If I decide to remove the code, it won&#8217;t be lost forever&#8230;I could pull it back in the future.  I could even tag the current version with &#8220;pre-dialog-removal&#8221; before stripping it out.  After consulting with Jamie the last option is the one I&#8217;ve chosen.  So, we can kill not just those pieces of code, we can also remove the <em>has_command</em> function, since it is only used in that part of the code.  Big win!</p>
<p>So, I&#8217;ll make a tagged copy before ripping stuff out:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">svn</span> <span style="color: #c20cb9; font-weight: bold;">cp</span> lib tags<span style="color: #000000; font-weight: bold;">/</span>lib<span style="color: #000000; font-weight: bold;">/</span>pre-dialog-removal</pre></div></div>

<p>Now I know I can always go back and refer to that code if I want to.  It&#8217;s not really particularly precious, but it&#8217;s a good practice to get into, since copies in Subversion are cheap and fast (likewise for git, and most other modern distributed revision control systems), and I never know when I might want to go back and see how something was done before.  I&#8217;ll do the same in the Webmin tree before I make the changes needed to merge the new OsChooser.pm in place of the old oschooser.pl.</p>
<p>So, after killing the dialog pieces of the code, and converting the user interaction to its own function, we have:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># ask for the operating system name ourselves</span>
<span style="color: #000000; font-weight: bold;">sub</span> ask_user <span style="color: #009900;">&#123;</span>
<span style="color: #b1b100;">my</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$names_ref</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$list_ref</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">@_</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@names</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">@$names_ref</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@list</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">@$list_ref</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$vnum</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$osnum</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$dashes</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot;-&quot;</span> x <span style="color: #cc66cc;">75</span>;
<span style="color: #000066;">print</span> <span style="color: #cc0000; font-style: italic;">&lt;&lt;EOF;
For Webmin to work properly, it needs to know which operating system
type and version you are running. Please select your system type by
entering the number next to it from the list below
$dashes
EOF</span>
<span style="color: #009900;">&#123;</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$i</span>;
<span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$i</span><span style="color: #339933;">=</span>0; <span style="color: #0000ff;">$i</span><span style="color: #339933;">&lt;</span><span style="color: #0000ff;">@names</span>; <span style="color: #0000ff;">$i</span><span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000066;">printf</span> <span style="color: #ff0000;">&quot; %2d) %-20.20s &quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$i</span><span style="color: #339933;">+</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$names</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">$i</span><span style="color: #009900;">&#93;</span>;
  <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$i</span><span style="color: #339933;">%</span>3 <span style="color: #339933;">==</span> <span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span>;
  <span style="color: #009900;">&#125;</span>
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$i</span><span style="color: #339933;">%</span>3<span style="color: #009900;">&#41;</span>;
<span style="color: #009900;">&#125;</span>
<span style="color: #000066;">print</span> <span style="color: #0000ff;">$dashes</span><span style="color: #339933;">,</span><span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Operating system: &quot;</span>;
<span style="color: #000066;">chop</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$osnum</span> <span style="color: #339933;">=</span> <span style="color: #009999;">&lt;STDIN&gt;</span><span style="color: #009900;">&#41;</span>;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$osnum</span> <span style="color: #339933;">!~</span> <span style="color: #009966; font-style: italic;">/^\d+$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;ERROR: You must enter the number next to your operating<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
  <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;system, not its name or version number.<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
  <span style="color: #000066;">exit</span> <span style="color: #cc66cc;">9</span>;
  <span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$osnum</span> <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">1</span> || <span style="color: #0000ff;">$osnum</span> <span style="color: #339933;">&gt;</span> <span style="color: #0000ff;">@names</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;ERROR: $osnum is not a valid operating system number.<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
  <span style="color: #000066;">exit</span> <span style="color: #cc66cc;">10</span>;
  <span style="color: #009900;">&#125;</span>
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
&nbsp;
<span style="color: #666666; font-style: italic;"># Ask for the operating system version</span>
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$name</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$names</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">$osnum</span><span style="color: #339933;">-</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span>;
<span style="color: #000066;">print</span> <span style="color: #cc0000; font-style: italic;">&lt;&lt;EOF;
Please enter the version of $name you are running
EOF</span>
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;Version: &quot;</span>;
<span style="color: #000066;">chop</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$vnum</span> <span style="color: #339933;">=</span> <span style="color: #009999;">&lt;STDIN&gt;</span><span style="color: #009900;">&#41;</span>;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$vnum</span> <span style="color: #339933;">!~</span> <span style="color: #009966; font-style: italic;">/^\S+$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;ERROR: An operating system number cannot contain<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
  <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;spaces. It must be like 2.1 or ES4.0.<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
  <span style="color: #000066;">exit</span> <span style="color: #cc66cc;">10</span>;
  <span style="color: #009900;">&#125;</span>
<span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
<span style="color: #000066;">return</span> <span style="color: #009900;">&#91;</span> <span style="color: #0000ff;">$name</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$vnum</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">$NAMES_TO_REAL</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$name</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$vnum</span> <span style="color: #009900;">&#93;</span>;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Not too bad.  It just fits into one 50 row editor window without scrolling, so that&#8217;s a small enough bite for me.  We make use of the %NAMES_TO_REAL global in this function, to convert from the short names to the longer human-friendly names, and I&#8217;m beginning to get a vague feeling something could be done to encapsulate that functionality, even without making this an Object Oriented library (which seems like overkill for such a simple program), so I&#8217;ll probably be coming back to that global in a later post (and I thought I would have a hard time getting two full posts worth out of this exercise!).</p>
<p><strong>Wrapping Up</strong></p>
<p>I&#8217;m feeling pretty good about the code now.  I think it&#8217;s more readable than before I started messing with it, it&#8217;s certainly shorter due to bits of refactoring and some removal of dead or redundant code, and it&#8217;s got quite a few tests.  All of its variables are reasonably scoped to the areas where they are used, except for %NAMES_TO_REAL, which is a package scoped <em>my</em> variable (turns out <em>eval</em> gets the scope of the containing block, so it doesn&#8217;t need to be an <em>our</em> variable as I&#8217;d first assumed).</p>
<p>The various utility functions aren&#8217;t very useful to outsiders and may change&#8230;the only function I really want to be public is <em>oschooser</em>, so I can see several opportunities for further enhancements, like encapsulating the rest into private methods within an OsChooser object.  But that&#8217;ll be a project for another day.  You can see the <a href="/src/OsChooser.pl">current code<a/>, plus an example <a href="/src/os_list.txt"><em>os_list.txt</em></a>.  Next time, I&#8217;ll begin work on wrapping this up for CPAN, and releasing a large OS definition list (Webmin&#8217;s list is incredibly long and detects hundreds of systems and versions, but needs a bit of massaging to be generically useful, due to its own internal version requirements).</p>
<p>Who knew one simple script could present so much interesting work?  It&#8217;s been so much fun, I think I&#8217;ll start a Perl Neighborhood Watch and do this to every little Perl script I come across.  Who&#8217;s with me!?  (Or maybe I should just focus on our own code for a little while longer, since we&#8217;ve got quite a few nooks and crannies that haven&#8217;t seen any attention in years.  Perl makes us lazy with its peskily perfect backward compatibility.)</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl-part-2/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Old School to New School: Refactoring&#160;Perl</title>
		<link>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl</link>
		<comments>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl#comments</comments>
		<pubDate>Fri, 25 Jul 2008 08:57:02 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Features]]></category>
		<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=27</guid>
		<description><![CDATA[At YAPC::NA I sat in on lots of great talks (I also won Randal Schwartz in the charity auction, and so got to be beaten soundly at pool by him, and learn a few things about Smalltalk and Seaside). In particular, Michael Schwern gave a fantastic talk entitled Skimmable Code: Fast to Read, Fast to [...]]]></description>
			<content:encoded><![CDATA[<p>At YAPC::NA I sat in on lots of great talks (I also won Randal Schwartz in the charity auction, and so got to be beaten soundly at pool by him, and learn a few things about Smalltalk and Seaside).  In particular, Michael Schwern gave a fantastic talk entitled <a href="http://conferences.mongueurs.net/yn2008/talk/1131">Skimmable Code:  Fast to Read, Fast to Change‎</a>.  This got me thinking about our own code.  Webmin is an old codebase, approaching 11 years old, and thus has some pretty old school Perl practices throughout.  Coding standards sort of stick to projects over a few years, and as new code comes in, it tends to look like the old code.  And, to add to that momentum, Jamie has religiously kept compatibility for module authors throughout the entire life of the project.  Modules written ten years ago can, astonishingly, be expected to work identically in todays Webmin, though they might not participate in logging or advanced ACLs or other nifty features that have come to exist in the framework in that time.</p>
<p>So, when I found myself needing to make a modification to <em>oschooser.pl</em>, a small program for detecting the operating system on which Webmin is running (sounds trivial, but when you realize that Webmin runs on <em>hundreds</em> of operating systems and versions, it turns out to be a rather complex problem), I decided to take the opportunity to put into practice some of the niceties of modern Perl.  This article is a little different than what I usually write for <em>In the Box</em>, in that it covers a lot of ground fast, and most of it is probably pretty mundane stuff for folks already writing modern Perl.  But, I think there&#8217;s enough old Perl code running around out there, running the Internet and such, that it&#8217;s worth talking about modernization work.</p>
<p>So, let&#8217;s go spelunking!</p>
<p><strong>Introduction to oschooser.pl</strong></p>
<p>The code we&#8217;ll be picking apart, and putting back together, is probably one of the more heavily used pieces of Perl code, and certainly one of the oldest, in the wild.  It&#8217;s the OS detection code that Webmin and Usermin use to figure out what system they&#8217;re running on during installation.  With Webmin having 12 million (give or take several million) downloads over its ten year history, this equals a <em>lot</em> of operating systems successfully detected.  Perhaps I should have picked something a little less important for my first stab at modernization, but I&#8217;ve rarely been accused of being smart about making sweeping changes!  (Jamie will reel me in, before I break actual Webmin code.  I manage to break Virtualmin every now and then&#8230;but he&#8217;s more suspicious when I check code into Webmin, since it happens quite rarely.)</p>
<p>The <em>oschooser.pl</em> program actually loads up a rather complex definitions file called <em>os_list.txt</em> (by default, though it&#8217;s configurable, and we use different lists for Virtualmin and Webmin, since they have different requirements for version identification).  The definitions file can contain snippets of Perl code, which will be executed via <em>eval</em>, when appropriate.  Most of the updates to OS detection over the years have happened in <em>os_list.txt</em>, so <em>oschooser.pl</em> hasn&#8217;t seen a lot of grooming over the years, which makes it a prime candidate for modernization.  Assuming, of course, that it works identically when I&#8217;m done with it.</p>
<p><strong>Where to start?</strong></p>
<p>My end goal with this project is to make <em>oschooser.pl</em> usable as a library from Perl programs, since our new product installer is written in Perl rather than POSIX shell.  I also figured it&#8217;d be nice to make it testable, since I&#8217;ve made several mistakes in the detection code (in <em>os_list.txt</em>, specifically) over the past few years that led to our product being uninstallable on some systems until the bug was tracked down.  But, first things first.  Almost nothing in Webmin is strict compatible, and even warnings can cause some complaints, so that seems like a good starting point.</p>
<p>The code we&#8217;re starting with can be found <a href="http://inthebox.webmin.com/src/oschooser.pl">here</a>, so you can follow along at home.</p>
<p>Enabling warnings reveals the following (don&#8217;t worry about the arguments for now):</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">perl</span> <span style="color: #660033;">-w</span> oschooser.pl os_list.txt outfile <span style="color: #000000;">1</span>
Name <span style="color: #ff0000;">&quot;main::uname&quot;</span> used only once: possible typo at oschooser.pl line 31.
Name <span style="color: #ff0000;">&quot;main::donename&quot;</span> used only once: possible typo at oschooser.pl line 17.</pre></div></div>

<p>Not too bad, actually.  Just a couple of variables that are only seen once, easy enough to fix by giving them a<em> my</em> declaration.  Though, in this case, it looks like enabling warnings turns up some unused code.  While <em>donename</em> is actually keeping track of what names we&#8217;ve seen, so far, and it&#8217;s one of several idiomatic ways to build an array of unique values, the <em>uname </em>variable seems to have no purpose.  So I&#8217;m going to kill that whole line rather than declare it.</p>
<p>Next up in our &#8220;low-hanging fruit&#8221; exercise is enabling <em>use strict.</em> Turns out this is quite a lot more intimidating:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">perl</span> <span style="color: #660033;">-c</span> oschooser.pl
Global symbol <span style="color: #ff0000;">&quot;$oslist&quot;</span> requires explicit package name at oschooser.pl line 15.
Global symbol <span style="color: #ff0000;">&quot;$out&quot;</span> requires explicit package name at oschooser.pl line 15.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line 15.
Global symbol <span style="color: #ff0000;">&quot;$oslist&quot;</span> requires explicit package name at oschooser.pl line 16.
Global symbol <span style="color: #ff0000;">&quot;$oslist&quot;</span> requires explicit package name at oschooser.pl line 16.
Global symbol <span style="color: #ff0000;">&quot;@list&quot;</span> requires explicit package name at oschooser.pl line 20.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line 21.
Global symbol <span style="color: #ff0000;">&quot;%names_to_real&quot;</span> requires explicit package name at oschooser.pl line 22.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line 27.
Global symbol <span style="color: #ff0000;">&quot;$etc_issue&quot;</span> requires explicit package name at oschooser.pl line 30.
Global symbol <span style="color: #ff0000;">&quot;$etc_issue&quot;</span> requires explicit package name at oschooser.pl line 33.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line 36.
Global symbol <span style="color: #ff0000;">&quot;@list&quot;</span> requires explicit package name at oschooser.pl line 36.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line 37.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line 37.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 39.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line 39.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 40.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 41.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line 41.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 41.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 43.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 44.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line 44.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 44.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line 49.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 53.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line 54.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line 59.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line 61.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line 67.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line 72.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line 77.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line 80.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 81.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 81.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line 81.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 81.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line 82.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 82.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line 82.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 82.
Global symbol <span style="color: #ff0000;">&quot;$tmp_base&quot;</span> requires explicit package name at oschooser.pl line 84.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line 85.
Global symbol <span style="color: #ff0000;">&quot;$tmp_base&quot;</span> requires explicit package name at oschooser.pl line 85.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line 86.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line 86.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 87.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line 87.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 88.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 88.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 89.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line 96.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line 96.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 96.
Global symbol <span style="color: #ff0000;">&quot;@vers&quot;</span> requires explicit package name at oschooser.pl line 97.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line 97.
Global symbol <span style="color: #ff0000;">&quot;@list&quot;</span> requires explicit package name at oschooser.pl line 97.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line 98.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 99.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 99.
Global symbol <span style="color: #ff0000;">&quot;@vers&quot;</span> requires explicit package name at oschooser.pl line 99.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 99.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line 100.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 100.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line 100.
Global symbol <span style="color: #ff0000;">&quot;@vers&quot;</span> requires explicit package name at oschooser.pl line 100.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 100.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line 102.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line 102.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 103.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line 103.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 104.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 104.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line 105.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 106.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 110.
Global symbol <span style="color: #ff0000;">&quot;@vers&quot;</span> requires explicit package name at oschooser.pl line 110.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 110.
Global symbol <span style="color: #ff0000;">&quot;$dashes&quot;</span> requires explicit package name at oschooser.pl line 114.
Global symbol <span style="color: #ff0000;">&quot;$dashes&quot;</span> requires explicit package name at oschooser.pl line 115.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 121.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 121.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line 121.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 121.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 122.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line 122.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 122.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 123.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line 125.
Global symbol <span style="color: #ff0000;">&quot;$dashes&quot;</span> requires explicit package name at oschooser.pl line 126.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 128.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 129.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 134.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 134.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line 134.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 135.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line 141.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line 141.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line 141.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line 142.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 146.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 147.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 153.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line 153.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 153.
Global symbol <span style="color: #ff0000;">&quot;%names_to_real&quot;</span> requires explicit package name at oschooser.pl line 154.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line 154.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line 154.
Global symbol <span style="color: #ff0000;">&quot;$out&quot;</span> requires explicit package name at oschooser.pl line 159.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 160.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 161.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 162.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line 163.
Global symbol <span style="color: #ff0000;">&quot;$d&quot;</span> requires explicit package name at oschooser.pl line 170.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line 172.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line 174.
Global symbol <span style="color: #ff0000;">&quot;$d&quot;</span> requires explicit package name at oschooser.pl line 177.
Global symbol <span style="color: #ff0000;">&quot;$d&quot;</span> requires explicit package name at oschooser.pl line 178.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line 178.
Global symbol <span style="color: #ff0000;">&quot;$d&quot;</span> requires explicit package name at oschooser.pl line 178.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line 181.
oschooser.pl had compilation errors.</pre></div></div>

<p>Wow!  I think that might be more lines than the program itself.  Luckily, it&#8217;s almost entirely unscoped variables.  A quick pass over the code, adding <em>my</em> declarations to the obvious candidates, gets things looking a little better.  One tricky bit is the <em>$i</em> loop variables used in <em>for</em> loops.  We don&#8217;t want those to be declared several times in the code, and we don&#8217;t want them to leak out into the scope of the rest of the program.  In modern Perl, this is no problem, as you can use the following:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;">    <span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span><span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$i</span><span style="color: #339933;">=</span>0; <span style="color: #0000ff;">$i</span><span style="color: #339933;">&lt;</span><span style="color: #0000ff;">@names</span>; <span style="color: #0000ff;">$i</span><span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #0000ff;">$cmd</span> .<span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot; &quot;</span>.<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$i</span><span style="color: #339933;">+</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span>.<span style="color: #ff0000;">&quot; '$names[$i]'&quot;</span>;
      <span style="color: #009900;">&#125;</span></pre></div></div>

<p>And <em>$i</em> will be local to the <em>for</em> loop.  I momentarily feared that I&#8217;d need to use an outer block to accomplish this, as Webmin needs to be compatible with quite old Perl versions (5.005, for core Webmin, unless Unicode support is needed, in which case 5.8.1 is required), but after downloading and installing Perl 5.005_4, I found that was an unnecessary precaution.  The <em>foreach</em> loops can also make use of this convenient feature.  If you do happen to be stuck with an even more ancient version that 5.005 (but still higher than 4)&#8211;though I can&#8217;t imagine how you could, as 5.005 is over nine years old&#8211;you can use the following:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;">  <span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$i</span>;
    <span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$i</span><span style="color: #339933;">=</span>0; <span style="color: #0000ff;">$i</span><span style="color: #339933;">&lt;</span><span style="color: #0000ff;">@names</span>; <span style="color: #0000ff;">$i</span><span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #0000ff;">$cmd</span> .<span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot; &quot;</span>.<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$i</span><span style="color: #339933;">+</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span>.<span style="color: #ff0000;">&quot; '$names[$i]'&quot;</span>;
      <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>Which provides similar private scope for the <em>$i</em> variable, at the cost of three extra lines.</p>
<p><strong>Making it Testable</strong></p>
<p>So far, I haven&#8217;t made any changes that are likely to break the code.  It&#8217;s merely been cleanup and syntax tweaks.  But, to accomplish everything I&#8217;d like in this exercise, we&#8217;ll be doing some refactoring and refining the code.  To do that with confidence, it&#8217;d be nice to have some tests to insure the code works the same before and after any changes.</p>
<p>Since this is not historically a library, it&#8217;s not particularly easy to test.  One could write a custom test harness, or use Test::Command, and test its behavior as a whole, but since it&#8217;s written in Perl and one of my goals is to make it useful as a library from Perl scripts, I decided instead to make it loadable as a module and use Test::More.  A trick that&#8217;s very common in the Python world, but doesn&#8217;t seem as well-known amongst Perlmongers is a <em>main</em> function which is called if the script is executed independently rather than via <em>use</em> or <em>require</em>.  The <em>main</em> function then calls whatever the script would normally do, optionally setting up variables or parsing command line arguments.</p>
<p>So, I added the following near the beginning of the file:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;"># main</span>
<span style="color: #000000; font-weight: bold;">sub</span> main<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>$#ARGV <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> <span style="color: #000066;">die</span> <span style="color: #ff0000;">&quot;Usage: $0 os_list.txt outfile [0|1|2|3]<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>; <span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">my</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$oslist</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$out</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$auto</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">@ARGV</span>;
oschooser<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$oslist</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$out</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$auto</span><span style="color: #009900;">&#41;</span>;
<span style="color: #009900;">&#125;</span>
main<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">unless</span> <span style="color: #000066;">caller</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;  <span style="color: #666666; font-style: italic;"># make it testable and usable as a library</span></pre></div></div>

<p>I also took this opportunity to add a simple usage message if the command is executed with fewer than two arguments (@ARGV, like all Perl arrays starts counting at 0).  I also needed to wrap the main function of the script in a <em>sub</em> block, so that the script doesn&#8217;t do anything immediately if loaded as a library.</p>
<p><strong>Make it a Module</strong></p>
<p>Since I want to use this code as a library, I face a choice.  The <em>use</em> statement is functionally equivalent to:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">BEGIN</span> <span style="color: #009900;">&#123;</span> <span style="color: #000066;">require</span> Module; Module<span style="color: #339933;">-&gt;</span><span style="color: #006600;">import</span><span style="color: #009900;">&#40;</span> LIST <span style="color: #009900;">&#41;</span>; <span style="color: #009900;">&#125;</span></pre></div></div>

<p>Which means, I suppose, I could keep the name oschooser.pl and use:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #000066;">require</span> <span style="">'oschooser.pl'</span>;</pre></div></div>

<p>We don&#8217;t need BEGIN level assurance, since we have no prototypes in this library and only use simple subroutines.  But, I find this a bit unsatisfying, since it&#8217;s no longer in common use amongst Perl developers, and <em>use</em> provides the ability to export functions explicitly.  Test::More has both a <em>use_ok</em> and a <em>require_ok</em> function, so it&#8217;s irrelevant from a testing perspective.  It&#8217;ll probably remain <em>oschooser.pl</em> in Webmin proper, and <em>OsChooser.pm</em> in my Virtualmin installer library, at least for the foreseeable future.  Not really a lot of difference between the two.</p>
<p><strong>Some Tests</strong></p>
<p>So, now that we can call the library roughly the way we want, using <em>use</em>, it&#8217;s time to write a few tests to be sure things actually work after we begin making more sweeping changes.</p>
<p>We can start with simple compile tests (I usually call these types of tests <em>t/return.t</em>, as they just check to be sure the module returns without error on load and the functions within return the data type that is expected):</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/usr/bin/perl -w</span>
<span style="color: #666666; font-style: italic;"># These tests just check to be sure all functions return something</span>
<span style="color: #666666; font-style: italic;"># It doesn't care what it is returned...so garbage can still pass,</span>
<span style="color: #666666; font-style: italic;"># as long as the garbage is the right data type.</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">use</span> strict;
<span style="color: #000000; font-weight: bold;">use</span> Test<span style="color: #339933;">::</span><span style="color: #006600;">More</span> <span style="color: #000066;">qw</span><span style="color: #009900;">&#40;</span>no_plan<span style="color: #009900;">&#41;</span>;
&nbsp;
use_ok<span style="color: #009900;">&#40;</span> <span style="">'OsChooser'</span> <span style="color: #009900;">&#41;</span>;
&nbsp;
isa_ok<span style="color: #009900;">&#40;</span>\OsChooser<span style="color: #339933;">::</span><span style="color: #006600;">have_tty</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="">'SCALAR'</span><span style="color: #009900;">&#41;</span>;
isa_ok<span style="color: #009900;">&#40;</span>\OsChooser<span style="color: #339933;">::</span><span style="color: #006600;">has_command</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;cp&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="">'SCALAR'</span><span style="color: #009900;">&#41;</span>;</pre></div></div>

<p>Hmm&#8230;OK, so we don&#8217;t actually have a lot to test yet, just a couple of utility functions (and I&#8217;ve even cheated a little and looked ahead to where I introduced a <em>have_tty</em> function, or this would be an even shorter set of tests).  The most important function, <em>oschooser</em>, doesn&#8217;t know how to return anything very useful yet.  It can only write out its findings to a file.  But, since we&#8217;re always going to be creating that file, regardless of how nice the module usage becomes, we need to figure out how to test it anyway.</p>
<p>Unsurprisingly, there is already a full-featured module on CPAN for testing the contents of files, called, unlikely though it may seem, <em>Test::Files</em>.  So, we&#8217;ll just grab that:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">perl</span> <span style="color: #660033;">-MCPAN</span> <span style="color: #660033;">-e</span> shell
&nbsp;
cpan shell <span style="color: #660033;">--</span> CPAN exploration and modules installation <span style="color: #7a0874; font-weight: bold;">&#40;</span>v1.7602<span style="color: #7a0874; font-weight: bold;">&#41;</span>
ReadLine support enabled
&nbsp;
cpan<span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #c20cb9; font-weight: bold;">install</span> Test::Files
...</pre></div></div>

<p>And then create as many Operating System definition files as we want in the <em>t</em> directory.  We&#8217;ll just name them for the OS they represent.  This is the kind of testing I love, because the actual test file will be extremely simple, no matter how many operating systems I want to test on:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/usr/bin/perl -w</span>
<span style="color: #000000; font-weight: bold;">use</span> strict;
<span style="color: #000000; font-weight: bold;">use</span> OsChooser;
&nbsp;
<span style="color: #666666; font-style: italic;"># Get a list of the example OS definition files</span>
<span style="color: #000066;">opendir</span><span style="color: #009900;">&#40;</span>DIR<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;t/&quot;</span><span style="color: #009900;">&#41;</span> || <span style="color: #000066;">die</span> <span style="color: #ff0000;">&quot;can't opendir t/ $!&quot;</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@files</span> <span style="color: #339933;">=</span> <span style="color: #000066;">grep</span> <span style="color: #009900;">&#123;</span> <span style="color: #009966; font-style: italic;">/\.os/</span> <span style="color: #009900;">&#125;</span> <span style="color: #000066;">readdir</span><span style="color: #009900;">&#40;</span>DIR<span style="color: #009900;">&#41;</span>;
<span style="color: #000066;">closedir</span> DIR;
<span style="color: #000000; font-weight: bold;">use</span> Test<span style="color: #339933;">::</span><span style="color: #006600;">More</span> <span style="color: #000066;">qw</span><span style="color: #009900;">&#40;</span>no_plan<span style="color: #009900;">&#41;</span>;
<span style="color: #000000; font-weight: bold;">use</span> Test<span style="color: #339933;">::</span><span style="color: #006600;">Files</span>;
&nbsp;
<span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$file</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">@files</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #0000ff;">$file</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">/(.*)\.os$/</span>;
  <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$osname</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">$1</span>;
  <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$outfile</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot;t/outfile&quot;</span>;
  OsChooser<span style="color: #339933;">::</span><span style="color: #006600;">oschooser</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;os_list.txt&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$outfile</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span>;
  compare_ok<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;t/$file&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$outfile</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">$osname</span><span style="color: #009900;">&#41;</span>;
&nbsp;
  <span style="color: #666666; font-style: italic;"># Cleanup</span>
  <span style="color: #000066;">unlink</span> <span style="color: #0000ff;">$outfile</span>;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>I love data-driven software, and this is a fun little example of it.  We can run as many tests as we want, merely by adding more OS data files&#8211;one with the &#8220;os&#8221; suffix to provide what <em>should</em> be output by <em>oschooser</em> and one to contain the file that <em>oschooser</em> would normally use to identify the OS (<em>/etc/issue</em>, among others), which isn&#8217;t yet supported, but I&#8217;ll talk about it in the next post.  Speaking of being data-driven, I think it&#8217;d also be pretty nifty to get the test count from the <em>@files</em> array, rather than using no_plan, but because modules loaded with <em>use</em> are loaded early during compile time (in a BEGIN block, effectively) we don&#8217;t actually have anything in <em>@files</em> yet.</p>
<p>However, as mentioned, the <em>oschooser</em> function doesn&#8217;t yet allow one to specify the <em>issue</em> file to look at, so no matter how many definitions I provide, it&#8217;ll never be able to test anything but the OS the test is running on.  Oh, well, for now we&#8217;ll just create one OS definition file that matches my current OS, and make it a priority to make the function more testable somehow, possibly via an optional parameter to <em>oschooser</em>.</p>
<p>Alright, so now that we have some rudimentary tests in place, we can break stuff with confidence!  We&#8217;ll come back to testing again in the near future, since we&#8217;re leaving so much untested right now.</p>
<p><strong>Plain Old Documentation</strong></p>
<p>I&#8217;m going to take a quick detour now that we&#8217;ve got some basic tests in place.  Testing is one practice that most developers agree makes for great code, and the other practice that most folks can agree on is documentation.</p>
<p>Since this is such a simple piece of code, and was intended exclusively for use during installation of Webmin and Usermin, Jamie never really documented it.  Now that I&#8217;m forcing it to be useful in other locations, and having some fun giving it a modern Perl face lift, it&#8217;s as good a time as any to add some documentation.  POD isn&#8217;t the only documentation format usable within Perl code, but it is, by far, the most popular, and it has lots of great tools for processing and testing coverage, so that&#8217;s what Jamie recently chose for use in documenting the Virtualmin API.  It&#8217;s also easy to learn, and results in text that is pretty readable even before processing.</p>
<p>I&#8217;m not sure of the recommended practices for documenting scripts that work on both the command line and as a module, but here&#8217;s what I came up with:</p>

<div class="wp_syntax"><div class="code"><pre class="perl perl" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">=head1 OsChooser.pm
&nbsp;
Attempt to detect operating system and version, or ask the user to select
from a list.  Works from the command line, for usage from shell scripts,
or as a library for use within Perl scripts.
&nbsp;
=head2 COMMAND LINE USE
&nbsp;
OsChooser.pm os_list.txt outfile [auto]
&nbsp;
Where &quot;auto&quot; can be the following values:
&nbsp;
=over 4
&nbsp;
=item 0
&nbsp;
always ask user
&nbsp;
=item 1
&nbsp;
automatic, give up if fails
&nbsp;
=item 2
&nbsp;
automatic, ask user if fails
&nbsp;
=item 3
&nbsp;
automatic, ask user if fails and if a TTY
&nbsp;
=back
&nbsp;
=head2 SYNOPSIS
&nbsp;
    use OsChooser;
    my ($os_type, $version, $real_os_type, $real_os_version) =
       OsChooser-&gt;oschooser(&quot;os_list.txt&quot;, &quot;outfile&quot;, $auto, [$issue]);
&nbsp;
=cut</span></pre></div></div>

<p>Pretty simple, but covers the basics.</p>
<p><strong>Next Time</strong></p>
<p>Unfortunately, the code is now longer and probably a little less readable than before!  It&#8217;s probably more robust to changes, since it now has reasonably scoped variables.  And it&#8217;s more friendly to others who might want to use it, due to the new documentation and the ability to use it as a library in Perl or as a command in shell scripts.</p>
<p>Next time we&#8217;ll start in on the refactoring, and we&#8217;ll also write some more tests.  This is turning into a real challenge, due to the data-driven nature of the script, and the fact that it&#8217;s somewhat hardcoded to look for OS data in very specific locations.  Since, a big part of what I want to test is in the <em>os_list.txt</em> file, we don&#8217;t have the luxury of just saying, &#8220;It&#8217;s configuration&#8230;we&#8217;ll just make a special version for testing purposes.&#8221;  We&#8217;ll have to get far more clever.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>YAPC::NA</title>
		<link>http://inthebox.webmin.com/yapcna</link>
		<comments>http://inthebox.webmin.com/yapcna#comments</comments>
		<pubDate>Sat, 31 May 2008 01:53:13 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=26</guid>
		<description><![CDATA[Virtualmin, Inc. is sponsoring this years YAPC::NA, and Jamie and I will both be in attendance throughout the event. If you&#8217;re attending, drop us a line. We&#8217;ll have T-shirts (Virtualmin and VM2 logos in all sizes and a multitude of sweet colors!) and a bunch of Virtualmin products to give away.]]></description>
			<content:encoded><![CDATA[<p><a href="http://conferences.mongueurs.net/yn2008/index.html"><img src="http://inthebox.webmin.com/images/yapcna_2008_im_going.gif" border="0" alt="" /></a></p>
<p>Virtualmin, Inc. is sponsoring this years YAPC::NA, and Jamie and I will both be in attendance throughout the event.  If you&#8217;re attending, drop us a line.  We&#8217;ll have T-shirts (Virtualmin <em>and</em> VM2 logos in all sizes and a multitude of sweet colors!) and a bunch of Virtualmin products to give away.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/yapcna/feed</wfw:commentRss>
		<slash:comments>-1</slash:comments>
		</item>
		<item>
		<title>Community, or not&#160;community</title>
		<link>http://inthebox.webmin.com/community-or-not-community</link>
		<comments>http://inthebox.webmin.com/community-or-not-community#comments</comments>
		<pubDate>Tue, 08 Apr 2008 22:11:43 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Snippets]]></category>
		<category><![CDATA[Group of men at Ellis IslandFarmer turning sod]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/community-or-not-community</guid>
		<description><![CDATA[I chatted with an applicant to the Y Combinator Summer Founders Program this afternoon, and he brought up a really interesting question that I&#8217;d never really thought about before.  His question was, roughly, &#8220;Do community-based or value-based Y Combinator backed businesses do better?&#8221;  Let&#8217;s assume that a &#8220;community-based&#8221; business is one where the user base [...]]]></description>
			<content:encoded><![CDATA[<p>I chatted with an applicant to the <a href="http://www.ycombinator.com">Y Combinator</a> Summer Founders Program this afternoon, and he brought up a really interesting question that I&#8217;d never really thought about before.  His question was, roughly, &#8220;Do community-based or value-based Y Combinator backed businesses do better?&#8221;  Let&#8217;s assume that a &#8220;community-based&#8221; business is one where the user base creates the vast majority of the value (like reddit), and a &#8220;value-based&#8221; business is one where there is value even with only one user (like Zenter).  So, by this definition, a community-based site must reach a certain level of popularity before random folks on the Internet want to use it, while a value-based site can make life easier for a single person and that single person will keep coming back.</p>
<p>The reason this is interesting, to me, is that Y Combinator <em>likes</em> community-based companies, and my impression (admittedly supported by merely anecdotal and circumstantial evidence) is that they prefer them to value-based businesses, though this preference may be changing over time.  So, the question it raises is whether having a preference (even a mild one) for community-based businesses results in better outcomes for Y Combinator.  I suspect that it has a negative impact, but I don&#8217;t know that for sure.</p>
<p>We <em>can</em> break it down by exit, though:</p>
<p><strong>Community-based YC exits</strong></p>
<ul>
<li>Reddit &#8211; Rumored 12-ish million dollar acquisition by Condé Nast</li>
</ul>
<p><img src="/images/ellis-island-group.jpg" alt="Group of men at Ellis Island" width="465" height="335" /></p>
<p><strong>Value-based YC exits</strong></p>
<ul>
<li>Zenter &#8211; Undisclosed acquisition by Google</li>
<li>Parakey &#8211; Undisclosed acquisition by Facebook</li>
<li>Auctomatic &#8211; 5 million dollar cash+stock acquisition by Live Communicate</li>
<li>TextPayMe &#8211; Undisclosed acquisition by Amazon</li>
<li>Anywhere.fm* &#8211; Undisclosed acquisition by Imeem</li>
</ul>
<p><img src="/images/turning-sod.jpg" alt="Farmer turning sod" width="465" height="326" /></p>
<p>I&#8217;ve put an asterisk by Anywhere.fm above, as I&#8217;m certain some folks would consider it a community-based business&#8230;but it provides most of its value even with only one user, so it fits my requirements for a value-based business, and I think it&#8217;s fair to say they were not acquired based predominantly on the size of their community or traffic growth but on the strength of the application they&#8217;d built.</p>
<p>Of course, since all but one of those acquisitions in the &#8220;value-based&#8221; category are for an undisclosed sum, and I&#8217;m basing the reddit acquisition price on rumors (though rumors from disparate sources, I&#8217;ve never heard the numbers from any of the people actually involved), it&#8217;s hard to say with any confidence that the five exits in the value-based group are worth more to YC than the one community-based exit.  But, one can safely say that value-based businesses have made a lot more young YC-backed entrepreneurs rich than community-based businesses.</p>
<p>This isn&#8217;t the whole picture, of course.  Some of the biggest potential YC success stories have yet to have an exit.   It&#8217;s hard to argue that Loopt isn&#8217;t going to  be a big success story for YC, and it&#8217;s pretty much purely community-based, though they have the added hurdle of having to sell to mobile providers to attain their customer base.  Scribd has seen tremendous traffic growth and it&#8217;s a community-based site with a small amount of value independent of the community.  Obviously, &#8220;going viral&#8221; is a fantastic marketing opportunity that is only generally available to community-based businesses, but historically it seems that building a useful application is more likely to result in a successful exit than building a product that relies on a large community to have value.</p>
<p>I found this result surprising when I began listing off the exits and thinking about what kind of business they were.  I&#8217;ve always thought of Virtualmin as an outlier among YC companies, since we build a traditional installable web-based application, but when broken down into value vs. community, we&#8217;re not so far out.</p>
<p>Anyway, I don&#8217;t think I&#8217;ve stumbled onto anything earth-shaking in this little exercise, of course, but I do think it would be interesting to break down <em>all</em> YC companies (and perhaps even everything in the <a href="http://www.crunchbase.com/">TechCrunch Crunchbase</a>) into this categorization and see how things shake out with regard to who is still around and growing.  Maybe later&#8230;right now I&#8217;ve gotta go build some value.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/community-or-not-community/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
	</channel>
</rss>

