<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
    <channel>
        <title>General - Blogs at Near Infinity</title>


        <link>http://www.nearinfinity.com/blogs/</link>
        <description>Employee Blogs</description>
        <language>en</language>
        <copyright>Copyright 2012</copyright>
        <lastBuildDate>Tue, 29 Nov 2011 10:36:04 -0500</lastBuildDate>
        <generator>http://www.sixapart.com/movabletype/</generator>
        <docs>http://www.rssboard.org/rss-specification</docs>
        
        <item>
            <title>How To Get The Most From Twitter: Scoping Rules</title>
            <description><![CDATA[ <p>Ever noticed a tweet disappear? As in someone else can't see something you posted?  There's a common mistake even twitter pros make that causes this to occur.</p>
<p>Are you sure you're seeing <i>all</i> of the replies to your tweets -- even from people you don't follow?  Do you ever find yourself missing important posts -- like those from your real life friends?  Do you know which will reach a wider audience: new style retweets vs old style retweets?</p>
<p>This article will answer these and other twitter related questions. It will help beginner to intermediate twitter users get the most out of the service.</p>

<p><b>#1 Standard #Tweets</b></p>
<p><a href="http://4.bp.blogspot.com/-e8CL3yKse4c/TtRbWzwGNlI/AAAAAAAAByI/lB9vEdwttes/s1600/1.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="cursor:pointer; cursor:hand;width: 349px; height: 343px;" src="http://4.bp.blogspot.com/-e8CL3yKse4c/TtRbWzwGNlI/AAAAAAAAByI/lB9vEdwttes/s400/1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5680265477428229714" /></a></p>
<p>These appear simple.  These show up in the timeline of your followers (@admirer and @vet in the example above).  And they don't show up in the timeline of anyone who doesn't follow you (@effusive).  If this was all there was to twitter it would be a complete waste of time.</p>
<p>The subtlety with this type of tweet is that your content may show up to someone that doesn't follow you but that is following a hashtag or a topic you mention.  In the example above @hiro thows a hashtag onto the word aardvarks to indicate that it's special somehow.  His tweet is then picked up by @spca who runs a constant search in a twitter client for the word "#aardvark".</p>
<p>The first time I realized how powerful this could be I had was at a conference.  I was extremely bored by a terrible keynote.  With nothing else to do I discovered the conference hashtag on twitter and started watching it.  Suddenly I realized I wasn't alone.  I was in a huge room full of people making fun of the presenter!  It was like telepathy, or group consciousness or something.  I've never felt alone or bored at a conference since.</p>
<p>Interesting statistic: As of June 2010, about 65 million tweets were posted each day, equaling about 750 tweets sent each second. (<a href="http://en.wikipedia.org/wiki/Twitter">reference</a>)</p>
<p><b>#2 Replies</b></p>
<p><a href="http://3.bp.blogspot.com/-yg6C6yTNlfo/TtRbXFuUjsI/AAAAAAAAByU/Si6sfvHr-9c/s1600/2.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="cursor:pointer; cursor:hand;width: 400px; height: 317px;" src="http://3.bp.blogspot.com/-yg6C6yTNlfo/TtRbXFuUjsI/AAAAAAAAByU/Si6sfvHr-9c/s400/2.png" border="0" alt="" id="Img1" /></a></p>
<p>Replies are more complicated.  In the example above the guy on the left, @vet, replies to @hiro by prefixing his tweet with "@hiro".  However, this probably doesn't work like you think.  The tweet obviously shows up in @hiro's timeline, but it also shows up in @admirer's timeline because she follows both people mentioned in the tweet (@hiro <i>and</i> @vet).  But the tweet does <i>not</i> show up to @effusive because, while he follows @vet, the author of the tweet, he doesn't follow @hiro, the person being mentioned.</p>
<p>Getting this scoping rule wrong is way too easy.  For instance you can't just announce that someone did something great.  If @hiro started a tweet with "@vet gave a great anti-aardvark presentation" then most of his followers (e.g. @effusive) wouldn't actually see the tweet!  I've seen seasoned twitter users make this mistake.</p>
<p>As an interesting aside twitter can help track conversations.  If you click a reply button in a twitter client or on the website twitter will keep track of which tweet you replied to and then help piece a conversation together.  But if you simply start a new tweet with "@somone" without clicking reply, the above scoping rules still apply, but twitter won't help anyone reconstruct the conversation.</p>
<p>Finally a word on notifications: If someone mentions you (via a reply or anywhere in their tweet) and you follow them then you will get an e-mail and possibly an SMS notification.  If someone that you do not follow replies to you, you <i>will not</i> get an email.  For this reason you must either frequently check the "mentions and more" section of the website or use a column based twitter client, discussed in the final section on lists.</p>
<p>Interesting statistic: On average only 23% of tweets get a reply. (<a href="http://www.sysomos.com/insidetwitter/engagement/">more</a>)</p>
<p>Another one: 38% of tweets are conversational.  In other words 38% of tweets start with an "@". If your percentage of replies is lower you probably aren't using twitter to its fullest. (<a href="http://www.pearanalytics.com/blog/wp-content/uploads/2010/05/Twitter-Study-August-2009.pdf">more</a>)</p>
<p><b>#3 Reply-All's</b></p>
<p><a href="http://3.bp.blogspot.com/-VBvIcRq5ANk/TtRbXahnI2I/AAAAAAAAByc/U1KHy4J3x0Q/s1600/3.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="cursor:pointer; cursor:hand;width: 379px; height: 333px;" src="http://3.bp.blogspot.com/-VBvIcRq5ANk/TtRbXahnI2I/AAAAAAAAByc/U1KHy4J3x0Q/s400/3.png" border="0" alt="" id="Img2" /></a></p>
<p>So what happens if you want to reply to someone, but still have it show up in the timelines of <i>all</i> of your followers?  As long as your tweet doesn't start with an "@" symbol then you're fine.  So the convention that has grown up is to prefix your tweet with a "." and then the "@someone".  In the example above @effusive now sees the tweet where he didn't in a standard reply.</p>
<p>Interesting statistic: The highest usage rate of twitter to date occurred during the 2011 FIFA Women's World Cup Final between Japan and the United States when 7,196 tweets were published per second! (<a href="http://www.usatoday.com/tech/news/2011-07-18-world-cup-twitter-record_n.htm">more</a>)</p>
<p><b>#4 Old Style Retweet or "Retweet with Comment"</b></p>
<p><a href="http://4.bp.blogspot.com/-T2Z7KRpWud8/TtRbXh_DKuI/AAAAAAAABys/EHV8GHZIRrE/s1600/4.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="cursor:pointer; cursor:hand;width: 400px; height: 282px;" src="http://4.bp.blogspot.com/-T2Z7KRpWud8/TtRbXh_DKuI/AAAAAAAABys/EHV8GHZIRrE/s400/4.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5680265489838975714" /></a></p>
<p>Prior to late 2009 if you saw a tweet you wanted to share with your followers you would retweet (RT) it by copy and pasting their tweet and prefixing it with "RT @author".  The problem from a scoping perspective was that people would get duplicates.  In the example above @admirer sees a duplicate from @vet when he retweets @hiro's tweet.</p>
<p>There are other problems with old style retweets unrelated to scoping.  One problem is that you get long chains of "RT @person1 RT @person2 ...".  Another problem is it was hard to follow a person but not all their retweets.  The biggest problem is old style retweets take valuable characters away from your available 140, and sometimes require mangling the original.</p>
<p>Old style retweeting is still in use today primarily from people wish to retweet but add a comment, but also because it allows people to reach a wider audience (since new style retweets won't show up in searches or lists, more on this later).</p>
<p>Interesting statistic: Only 6% of tweets get retweeted (<a href="http://socialtimes.com/report-71-percent-of-tweets-do-not-get-a-single-reply-or-retweet_b24365">more</a>)</p>
<p><b>#5 New Style Retweet</b></p>
<p><a href="http://2.bp.blogspot.com/-OfR_u71pkK8/TtRbYFzvYkI/AAAAAAAABy4/n_JlWBmX-3Q/s1600/5.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="cursor:pointer; cursor:hand;width: 400px; height: 338px;" src="http://2.bp.blogspot.com/-OfR_u71pkK8/TtRbYFzvYkI/AAAAAAAABy4/n_JlWBmX-3Q/s400/5.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5680265499455218242" /></a></p>
<p>In November of 2009 twitter rolled out a new technique for retweeting aimed at solving the problems mentioned above.  It caused a lot of confusion, but did solve the problems.</p>
<p>The end result of new style retweets from a scoping perspective is that sometimes people see tweets from people they don't follow.  In the example above @effusive sees @hiro's tweet in his timeline even though he doesn't follow @hiro, because @vet performed a new-style retweet.</p>
<p>If you click the retweet button on the twitter website today it will perform a new style retweet.  Some twitter clients like TweetDeck give you the option.  The only way to perform an old style retweet or retweet with comment via the website is to copy and paste the message.</p>
<p>Interesting statistic: Less than half of tweets are posted using the web user interface.  By far the most common twitter client is Tweetdeck with 8.48% of the market. (<a href="http://www.sysomos.com/insidetwitter/clients/">more</a>)</p>
<p><b>#6 Direct Messages</b></p>
<p><a href="http://2.bp.blogspot.com/-hTvmYv4PA7k/TtRcUt8ld_I/AAAAAAAABzE/7gD3kRqy8Rc/s1600/6.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="cursor:pointer; cursor:hand;width: 400px; height: 276px;" src="http://2.bp.blogspot.com/-hTvmYv4PA7k/TtRcUt8ld_I/AAAAAAAABzE/7gD3kRqy8Rc/s400/6.png" border="0" alt="" id="Img4" /></a></p>
<p>Direct messages are simple.  Prefix your tweet with "D somone" (no at symbol before the name) and they will be the only person that will see it.  Just like e-mail except the recipient will get an e-mail and a text message if they've set that up on their phone.  Note that neither people watching an included hashtag  (e.g. @spca) nor people mentioned will see direct messages unless they are the recipient.</p>
<p>Interesting statistic: Six percent of all tweets are sent via SMS. (<a href="http://www.quora.com/What-percentage-of-Twitter-users-get-their-Twitter-updates-predominately-from-direct-text-messages-as-opposed-to-the-website-or-3rd-party-Apps">more</a>)</p>
<p><b>#7 Lists</b></p>
<p>The last scoping topic worthy of mention is lists.  This fantastic feature was added by twitter in late 2009.  It allows you to organize the people you follow.  For instance I have a private "Infrequent Posters" list that I try very hard to never miss a tweet from.  Meanwhile I follow enough people that my main list is more like a stream that I dip into but don't get stressed out if I miss some of.</p>
<p>Lists work best when used in conjunction with a column based twitter clients (e.g. TweetDeck).  These types of twitter clients are the only way to go if you want to become a twitter pro.  If you've gotten to the point where you start missing content from important people (e.g. your real life friends), or you're missing responses to your tweets, or mentions from people you don't follow: then you probably need lists, but you absolutely need something like TweetDeck.</p>
<p><a href="http://3.bp.blogspot.com/-xyrC1APLTqM/TtRcU6p58_I/AAAAAAAABzQ/cUvaNcE8_zk/s1600/7.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="cursor:pointer; cursor:hand;width: 368px; height: 321px;" src="http://3.bp.blogspot.com/-xyrC1APLTqM/TtRcU6p58_I/AAAAAAAABzQ/cUvaNcE8_zk/s400/7.png" border="0" alt="" id="Img3" /></a></p>
<p>In regards to scoping, lists contain one subtlety that you need to be aware of.  They do not support new style retweets.  In the example above @vet posts a normal tweet.  He then new-style retweets one of @hiro's tweets.  @effusive follows @vet and has also placed him into a list.  While the list helps @effusive not miss tweets from @vet, it does not end up displaying @hiro's tweet.  </p>
<p>Note that this rule applies both to people who maintain their own lists as well as to people who follow lists maintained by other people (e.g. your company may maintain a public list of its twitter enabled employees).</p>
<p>Interesting Statistic: Pointless babble is the most common type of content on twitter.  It represents 40.55% of all traffic. (<a href="http://www.pearanalytics.com/blog/wp-content/uploads/2010/05/Twitter-Study-August-2009.pdf">more</a>)</p>


<div style="float:right;padding:4px; text-align: center; width: 115px;">
<iframe src="http://platform.twitter.com/widgets/tweet_button.html#_=1322581831960&amp;count=horizontal&amp;id=twitter_tweet_button_0&amp;lang=en&amp;original_referer=http%3A%2F%2Frapidapplicationdevelopment.blogspot.com%2F2011%2F11%2Fhow-to-get-most-from-twitter.html&amp;related=lprichar%3ALee%20Richardson%20Blog&amp;text=RT%20%40lprichar%20How%20To%20Get%20The%20Most%20From%20Twitter%3A%20Understand%20Scoping%20Rules&amp;url=http%3A%2F%2Frapidapplicationdevelopment.blogspot.com%2F2011%2F11%2Fhow-to-get-most-from-twitter.html" allowtransparency="true" frameborder="0" scrolling="no" class="twitter-share-button twitter-count-horizontal" style="width: 110px; height: 20px; " title="Twitter For Websites: Tweet Button"></iframe>
<script type="text/javascript">
        var currentPageUrl = 'http://rapidapplicationdevelopment.blogspot.com/2011/11/how-to-get-most-from-twitter.html';

        /* DotNetKicks */
        var dotnetkicksLink = document.createElement('a');
        dotnetkicksLink.setAttribute('href', 'http://www.dotnetkicks.com/kick/?url=' + currentPageUrl);
        var dotnetkicksImg = document.createElement('img');
        dotnetkicksImg.setAttribute('src', 'http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=' + currentPageUrl);
        dotnetkicksImg.setAttribute('alt', 'Kick this article (a good thing) on DotNetKicks');
        dotnetkicksImg.setAttribute('border', '0');
        dotnetkicksImg.setAttribute('style', 'margin-left:auto; margin-right:auto; display:block; text-align:center;');
        dotnetkicksLink.appendChild(dotnetkicksImg);

        var div = document.createElement('div');
        div.appendChild(document.createElement('br'));
        div.appendChild(dotnetkicksLink);
        document.write(div.innerHTML);
    </script>
<p></p>
<div id="___plusone_0" style="height: 20px; width: 90px; display: inline-block; text-indent: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: transparent; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; float: none; line-height: normal; font-size: 1px; vertical-align: baseline; background-position: initial initial; background-repeat: initial initial; "><iframe allowtransparency="true" frameborder="0" hspace="0" id="I1_1322581832741" marginheight="0" marginwidth="0" name="I1_1322581832741" scrolling="no" src="https://plusone.google.com/_/+1/fastbutton?url=http%3A%2F%2Frapidapplicationdevelopment.blogspot.com%2F2011%2F11%2Fhow-to-get-most-from-twitter.html&amp;size=medium&amp;count=true&amp;annotation=bubble&amp;hl=en-US&amp;jsh=m%3B%2F_%2Fapps-static%2F_%2Fjs%2Fwidget%2F__features__%2Frt%3Dj%2Fver%3DJJ_UMfXdHS0.en_US.%2Fsv%3D1%2Fam%3D!7cXxqVkrvlyIEO88iA%2Fd%3D1%2F#id=I1_1322581832741&amp;parent=http%3A%2F%2Frapidapplicationdevelopment.blogspot.com&amp;rpctoken=152461457&amp;_methods=onPlusOne%2C_ready%2C_close%2C_open%2C_resizeMe" style="width: 90px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; height: 20px; position: static; left: 0px; top: 0px; visibility: visible; " tabindex="-1" vspace="0" width="100%" title="+1"></iframe></div>
<script src="http://platform.twitter.com/widgets.js" type="text/javascript">
</script>
</div>

<p><b>Conclusion</b></p>
<p>Twitter may appear to be fairly simple at first glance.  In reality it has grown over the years to become very sophisticated.  Sophisticated and incredibly powerful.  But to fully tap that power you must understand the scoping rules and subtleties surrounding things like replies, retweets, and lists.  Hopefully you're closer now.</p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/lee_richardson/how_to_get_the_most_from_twitt.html</link>
            <guid>http://www.nearinfinity.com/blogs/lee_richardson/how_to_get_the_most_from_twitt.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">twitter</category>
            
            <pubDate>Tue, 29 Nov 2011 10:36:04 -0500</pubDate>
        </item>
        
        <item>
            <title>Android : Browsing your PC from your Phone</title>
            <description><![CDATA[If you are like me and have a lot of music, videos, and pictures stored on an external hard drive that you want to browse from your Android smartphone than you have a couple options. Some are expensive (a WIFI hard drive such as <a href="http://www.seagate.com/www/en-us/products/network_storage/home-network-storage">this one</a>). Others require you to trust your data <a href="https://www.amazon.com/clouddrive/learnmore/">to the cloud</a> which you may or may not be comfortable with. And still others are needlessly complicated (setting up your own FTP server was one suggestion I got).<br /><br />After some searching I was able to compose a simple and free option, which, while a little primitive, certainly gets the job done.<br /><br />ES File Explorer is a free app available on the Android market:<br /><br />https://market.android.com/details?id=com.estrongs.android.pop<br /><br />Its primary goal is to let you to browse files on your phone. However it allows provides support for several other file sharing methods including share drives over a LAN using the simple sharing service available on Windows computers.<br /><br />From a Windows computer attached to your local network, simply right click on the drive or folder you want to share, and click "Sharing and Security" to open up the file sharing configuration. (More information <a href="http://support.microsoft.com/kb/304040">here</a>.)<br /><br />Now you can open ES File Explorer, and in the upper left corner select there is a drop down menu (defaults to "Local") that has a "LAN" option on it. Selecting LAN should display a list of shared folders on your local network.<br /><br />Now when I first did this it worked for folders on my laptop local drive, but did not work for the USB attached hard drive where I stored my file backups. I could see the share, but when I clicked on it in ES File Explorer <br /><br />Apparently there is some obscure registry setting that can prevent you from successfully sharing. Detailed instructions are <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;177078">here</a>.<br /><br />In summary: <br /><br /><ol><li>Open regedit.exe</li><li>Navigate to HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanmanServer\Parameters</li><li>If the IRPStackSize value is present, double click on it. If it is not present, create it by right clicking in the Parameters window, selecting New, then DWord Value. Type IRPStackSize (it is case sensitive!)</li><li>Change the base to "decimal"</li><li>Choose a value that is larger than the existing value (or if you just created it, higher than the default value of 15.)</li><li>Restart your computer and try it now.</li><li>You may need to increase IRPStackSize a second time if it does not work. The maximum value you can enter is 50. <br /></li></ol>All done! Now you should be able to browse arbitrary content on your computer from your phone!<br />]]></description>
            <link>http://www.nearinfinity.com/blogs/stephen_mouring_jr/android_browsing_your_pc_hard.html</link>
            <guid>http://www.nearinfinity.com/blogs/stephen_mouring_jr/android_browsing_your_pc_hard.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Android</category>
            
            <pubDate>Sat, 05 Nov 2011 13:11:46 -0500</pubDate>
        </item>
        
        <item>
            <title>Blueprint for a Server Status Capistrano Script</title>
            <description><![CDATA[
<p>
Capistrano is great to work with. It's simple, powerful and flexible. For the last couple of weeks I've been building a series of Capistrano tasks that check the status and configuration of our web servers. I'm pretty happy with the results, so I though I would share the basic structure of what I've come up with. I'm just going to provide a couple of tasks here, along with all of the support methods, to provide a framework, but the idea would be to expand it with whatever commands would be run to verify the health of a server.
</p>
<p>
There were several requirements that I had to keep in mind when I was designing this
</p>
<ol>
<li>The cap tasks should use the environment configuration files that we already have.</li>
<li>Because there are a large number of tasks we should be able to run them individually, or with a single command we should be able to run the entire suite.</li>
<li>The cap script should produce well formatted, easy to read output, so that it's clear what's broken and where.</li>
</ol>


<h3><br /></h3><h3>Control Support Methods</h3>
<p>
While building the validation tasks I found that I was having to do the same basic operations fairly often:
</p>
<ol>
<li>Issue a command on a remote host.</li>
<li>Parse the output of that command.</li>
<li>Issue more commands based on the output.</li>
</ol>

<p>
Or sometimes I just wanted to run a series of commands on one host, verifying that it was correct before moving on to the next. It's possible to make Capistrano work this way, but it's not well documented. By default, if you give Capistrano multiple commands to run on multiple hosts, the it will run the commands in host order. However what I really wanted is for Capistrano to run the commands in command order. 
</p>
<p>
For instance: given that we have host-A, host-B, host-C, and I want to run commands doA, doB, and doC by default Capistrano will run:
</p>
<pre>    HostA &gt; doA
    HostB &gt; doA
    HostC &gt; doA
    HostA &gt; doB
    HostB &gt; doB
    HostC &gt; doB
    HostA &gt; doC
    HostB &gt; doC
    HostC &gt; doC

</pre>
<p>But what I needed it to do was:</p>
<pre>    HostA &gt; doA
    HostA &gt; doB
    HostA &gt; doC
    HostB &gt; doA
    HostB &gt; doB
    HostB &gt; doC
    HostC &gt; doA
    HostC &gt; doB
    HostC &gt; doC

</pre>
<p>
By using the roles that we already have configured for our deploy tasks with Capistrano we were able to create a few support methods that issue commands in the order that we want.
</p>
<br />
<h4>each_host</h4>
<p>
The each_host method is used to iterate through our configured hosts. The method prints out the hostname (which is important for the look of the script's output) sets the current host, then yields the block that was passed into it.
</p>
<pre class="prettyprint">  def each_host
    roles[:web].each do |host|
      print_hostname host
      set(:current_host, host.host)
      yield host
    end
  end

</pre>
<br />
<h4>run_serial</h4>
<p>
The run_serial method runs the given command, but only on the current host, yielding the output of the command and the name of the output stream (:out or :err). The ssh channel we don't have much use for.
</p>
<pre class="prettyprint">  def run_serial(command)
    run command, :hosts =&gt; fetch(:current_host) do |chan, stream, data|
      yield stream, data
    end
  end

</pre>
<br />
<h4>run_primary</h4>
<p>
We also have a primary server set in our configuration. We can create a few more support methods that take advantage of the primary host, for issuing command on one host only.
</p>
<pre class="prettyprint">  def run_primary(command)
    run command, :hosts =&gt; fetch(:primary) do |chan, stream, data|
      set_current_host channel
      yield stream, data
    end
  end

</pre>
<br />
<h4>currently_primary?</h4>
<p>
Having a method that tells us if we're currently on the primary host is helpful as well.
</p>
<pre class="prettyprint">  def currently_primary?
    fetch(:current_host) == fetch(:primary).host
  end

</pre>
<br />
<h4>set_current_host</h4>
<p>
The current host can also be set from the ssh channel. This method is called from the run_primary method, but this needs to be called when we call the default run method.
</p>
<pre class="prettyprint">  def set_current_host(channel)
    host = channel.properties[:host]
    print_hostname host
    set(:current_host, host)
  end
</pre>
<p>
Now we can write tasks that execute in in command order, execute tasks just on the primary host, and as we're preforming checks we print out the current host, so we're able to keep track of where we are. <br /></p><p><br /></p><p><br /></p>

<h3>Printing Support Methods</h3>
<p>
Because printing the script output out to the command line is an important part of the script I'm going to go over the printing support next. When running the server checks I wanted the output to look like rspec or cucumber. As each check is performed I wanted to see a colorful pass or fail message with the values that I was checking against to be highlighted. Also once the checks were finished I wanted to see a list of everything that failed, grouped by host.
</p>
<br />
<h4>print_hostname</h4>
<p>
This is the print_hostname method that was mentioned above. Everytime we move to a different host this method prints out the hostname.
</p>
<pre class="prettyprint">  def print_hostname(name)
    puts "    Host: #{grey(name)} &gt;"
  end

</pre>
<br />
<h4>step</h4>
<p>
Also at the beginning of every task I wanted to print the task name and the number of the task.
</p>
<pre class="prettyprint">  def step(name)
    step = fetch(:step)
    puts "\n[#{step}] #{name}"
    set(:step, step+1)
  end

</pre>
<br />
<h4>failure</h4>
<p>
The failure method is called when a check fails. It prints the failure message, and stores it under the current hostname so that it can be printed again after all the checks have been done. If the expected or actual parameters are included it prints those along with the message, otherwise just the message is printed.
</p>
<pre class="prettyprint">  def failure(message, expected=nil, actual=nil)
    host = fetch(:current_host)
    complete = (expected || actual) ?
      "#{message} Expected: #{grey(expected)} Actual: #{grey(actual)}" :
      message
    errors = fetch(:errors)
    unless errors[host]
      errors[host] = []
    end
    errors[host] &lt;&lt; message
    log_item(complete, :fail)
  end

</pre>
<br />
<h4>pass</h4>
<p>
The pass method is called when a check passes.
</p>
<pre class="prettyprint">  def pass(message)
    log_item(message, :pass)
  end

</pre>
<br />
<h4>log_item</h4>
<p>
The log_item method is called by the pass and failure methods. The method includes a warn and empty status which can also be used by the tasks when printing their status.
</p>
<pre class="prettyprint">  def log_item(message, flag=nil)
    status = case flag
      when :pass : "      [#{green("PASS")}] "
      when :fail : "      [#{red("FAIL")}] "
      when :warn : "      [#{orange("WARN")}] "
      else "      "
    end
    puts "#{status}#{message}"
  end

</pre>
<br />
<h4>colorize</h4>
<p>
The methods below handle the coloring of the text as it's printed to the command line. This was actually pretty fun figuring out. Every script should have fancy colorized output. (and now every script of mine will, muaha ha ha)
</p>
<pre class="prettyprint">  def red(text)
    colorize(text, 31)
  end

  def orange(text)
    colorize(text, 33)
  end

  def green(text)
    colorize(text, 32)
  end

  def grey(text)
    colorize(text, 37)
  end

  def colorize(text, color_code)
    "\e[#{color_code}m#{text}\e[0m"
  end

</pre>



<h3><br /></h3><h3>Start and Finish Tasks</h3>
<p>
Now that all that's taken care of we can start writing some actual Capistrano tasks to take advantage of our fine grained control and fancy printing.
</p>
<br />These server check tasks all share common startup and finish tasks that need to be run before and after any one of the tasks are run, or when the entire suite is run. This is defined using Capistrano's :start and :finish callbacks, but should only be run for the check tasks.

<pre class="prettyprint">  TASK_LIST = [
    "check:all",
    "check:environment_variables",
    "check:middleware_versions",
    "check:apache_configuration"]

  on :start, 'check:setup', :only =&gt; TASK_LIST
  on :finish, 'check:print_errors', :only =&gt; TASK_LIST

</pre><h4>setup</h4>

  
<p>
The setup task handles whatever specific setup needs to be done, but at the very least the step and errors variables need to be defined. They're used by the printing methods. Also, all of the tasks below are inside of the check namespace.
</p>
<pre class="prettyprint">  namespace :check do

    task :setup do
      set :step, 1
      set :errors, {}
    end

</pre>
<br />
<h4>print_errors</h4>
<p>
The print errors method is run after all the checks have been run to print a convenient list of errors grouped by host. This keeps errors from being lost in the output.
</p>
<pre class="prettyprint">  task :print_errors do
    errors = fetch(:errors)
    if (errors.size() &gt; 0)
      print "\n ==== #{red 'Errors'} ===="
      errors.each do |host,list|
        print_hostname host
        list.each {|e| puts "      #{e}"}
      end
    end
  end

</pre>



<h3><br /></h3><h3>Validation Tasks</h3>
<p>
The meat of the script are in the validation tasks. Out current script has fifteen different tasks that do everything from checking environment variables, to verifying directory and file permissions, to checking network and database statuses. If it can be automated, we'll find a way to get it in. Rather then include concrete examples though I'm just going to include some skeleton methods to show the method structure to illustrate how the support methods are used together in the Capistrano tasks.
</p>
<br />
<h4>Simple Tasks</h4>
<p>
This task executes one command on each server and validates the output from a list of expected results. This form is used by our script to check environment variables, commands on the sudo list, and programs in the cron, all of which can be read and verified with one command. I'm just including the validateVariable, and getVariable methods here to show the pass and failure methods.
</p>
<pre class="prettyprint">  VARIABLES = [
    { :key =&gt; 'KEY', :value =&gt; /Expected Value/ },
    { :key =&gt; 'KEY', :value =&gt; /Expected Value/ },
    { :key =&gt; 'KEY', :value =&gt; /Expected Value/ }]

  task :simple do
    step "A Simple Check"
    run "env" do |channel, stream, data|
      set_current_host channel
      VARIABLES.each do |map|
        validateVariable(data, map)
      end
    end
  end

  def validateVariable(data, map)
    value = getVariable(data, map[:key])
    (value.match map[:value]) ?
      pass("Env #{grey(map[:key])} is set to #{grey(value)}") :
      failure("Env #{grey(map[:key])} is incorrect", map[:value].inspect, value)
  end

  def getVariable(data, key)
    result = data.match /^#{key}=(.*)/
    unless result
      failure "Env #{grey(key)} is not set."
      return nil
    end
    result[1]
  end

</pre>
<br />
<h4>Tasks that run multiple commands on the same host</h4>
<p>
Most of the tasks in our validation script run multiple commands on the same host, using the each_host and run_serial methods. This allows us to read files and act on the values in those files. It's alse used because the script output looks better organized when running tasks in host order. The task below itterates through a list of standard directories, then a list of configured directories. For each directory a run_serial command is executed to see if the directory exists, then once the lists have been gone through it's done again on the next host.
</p>
<pre class="prettyprint">  task :serial_example do
    step "Run multiple commands on a host"
    each_host do
      DIRECTORIES.each do |dir|
        verify_directory dir
      end

      [:deploy_to, :transfer_path, :cache_path].each do |key|
        dir = fetch(key)
        if (dir)
          verify_directory dir
        else
          falure "No directory configured for #{grey(key)}"
        end
      end

    end
  end

  def verify_directory(path)
    command = "[ -d #{path}] &amp;&amp; echo 'true' || echo 'false'"
    run_serial command do |stream, data|
      if (data.strip == 'true')
        pass "Verified that #{grey(path)} exists."
      else
        failure "Directory at #{grey(path)} does not exist."
      end
    end
  end

</pre>
<br />
<h4>Tasks using different server combinations</h4>
<p>
Here's a command that runs on the primary host, reads a file, then acts across multiple hosts. We do something like this to verify that the Apache configuration is correct. We know the httpd.conf is the same across hosts, but we want to verify that the files and directories that are in the configuration are actually on the host in question.
</p>
<pre class="prettyprint">  task :primary_example do
    step "Verifying Certificates"
    path = fetch(:path_to_httpd_conf)
      certs = {}
      run_primary "grep SSLC[eA] #{path}" do |stream,data|

        if (stream == :err)
          failure "No http configuration file at #{grey(path)}"
          break
        end

        keys = [
          'SSLCertificateFile',
          'SSLCertificateKeyFile',
          'SSLCACertificatePath',
          'SSLCARevocationPath']

        keys.each do |key|
          certs[key] = read_http_conf_value(data,key)
        end

        pass "Read Certificate Paths from HTTP Configuration"
      end

      each_host do
        verify_file certs['SSLCertificateFile']
        verify_file certs['SSLCertificateKeyFile']
        verify_directory certs['SSLCACertificatePath']
        verify_directory certs['SSLCARevocationPath']
      end
    end

</pre>
<br />
<p>
And finally we need a task that runs the whole suite of validation tasks. The all task does nothing itself, it just runs all of the other tasks.
</p>
<pre class="prettyprint">  desc "Run all of the validation tasks"
  after "check:all",
    "check:environment_variables",
    "check:middleware_versions",
    "check:apache_configuration"

  task :all do
  end

</pre>
<p>
Then to execute the cap task you would type is:
</p>
<pre>cap -q production check:all
</pre>
<p>
And watch as all the beautiful passes and fails scroll by, though hopefully more of the former.
</p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/john_cato/blueprint_for_a_server_status.html</link>
            <guid>http://www.nearinfinity.com/blogs/john_cato/blueprint_for_a_server_status.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
            <pubDate>Thu, 28 Apr 2011 18:40:02 -0500</pubDate>
        </item>
        
        <item>
            <title>Modeling multiple logical views of a document</title>
            <description><![CDATA[<p align="LEFT" style="margin-bottom: 0in"><font size="3" style="font-size: 13pt"><font size="3"><b>Problem:</b></font><b>
</b><font size="3"><span style="font-weight: normal"> The domain model
contains the idea of a document, and one of the documents in the
model has multiple purposes. By purpose, I mean that the physical
document is filled out with different data depending on who the
document is for. Each purpose may or may not have different logic
associated with the creation. The document abstractions should be
easily testable to make document creation as robust as possible.</span></font></font></p>
<p align="LEFT" style="margin-bottom: 0in"><font size="3" style="font-size: 13pt"><font size="3"><span style="font-weight: normal">	</span></font><font size="3"><b>A
Solution: </b></font><font size="3"><span style="font-weight: normal">One
solution that I found was based on the template method from the GoF
book. The intent of this solution was to simplify the creation of the
known views of the document. The key idea of the solution is to put
as much of the logic involved in document creation into to the base
class. </span></font></font>
</p>
<p align="LEFT" style="margin-bottom: 0in"><font size="3" style="font-size: 13pt"><font size="3"><span style="font-weight: normal">	</span></font><font size="3"><b>Implementation:
</b></font></font>
</p>
<pre class="prettyprint">abstract class Document
{
	IDictionary<string, string=""> FormTextFields
	{
		get
		{
			return new Dictionary<string, string="">
			{
				{ "NameField", this.NameField },
				{ "AgeField", this.AgeField }
			};
		}
	}
	
	private string NameField
	{
		get
		{
			if(this.DocumentType == DocumentType.Big)
			{
				return "Big name";
			}
			else if(this.DocumentType == DocumentType.Small)
			{
				return "Small name";
			}
			
			return string.Empty;
		}
	}
	
	private string AgeField
	{
		get
		{
			if(this.DocumentType == DocumentType.Big)
			{
				return "42";
			}
			
			return string.Empty;
		}
	}
	
	protected abstract DocumentType DocumentType { get; }
}

class BigDocument : Document
{
	protected override DocumentType DocumentType
	{
		get
		{
			return DocumentType.Big;
		}
	}
}
</string,></string,></pre>
<p align="LEFT" style="margin-bottom: 0in"><font size="3" style="font-size: 13pt"><font size="3"><b>	Benefits:
</b></font><font size="3"><span style="font-weight: normal">As
previously stated, the main purpose of the base class is to simplify
the creation of the subclasses. This solution does just that by
reducing the amount of work needed to create a new subclass. The
subclasses have only two main actions required for creation. The
first action is to define the type of the document which is an
abstract method in the base class. The second action, not shown in the subclass, is the
validation of the specific concrete document type. Just doing these
two actions, as oppose to adding new creation logic, speeds up the
creation of new subclasses.</span></font></font></p>
<p align="LEFT" style="margin-bottom: 0in"><font size="3" style="font-size: 13pt"><font size="3"><span style="font-weight: normal">	Two
other benefits to this solution are centralization of the logic for
document creation and having reduced unit testing needs for the subclasses.
When the logic of the document creation is centralized into a single
base class, reasoning about the document creation becomes easier. Problems arising from document creation are easier to track down. The
document base class becomes the focal point for the majority of  the
unit tests written for document creation. The focal point reduces the
need to test the subclasses since the vast majority of the behavior
is in the base.</span></font></font></p>
<p align="LEFT" style="margin-bottom: 0in"><font size="3" style="font-size: 13pt"><font size="3"><span style="font-weight: normal">	</span></font><font size="3"><b>Drawbacks:</b></font><font size="3"><span style="font-weight: normal">
A large drawback of placing all the logic relating to the document
creation in the base is that the rules for creation have to be
completely available. If the rules of the creation of the document
are not known then the base class will be incomplete. When the base
class is incomplete, it has to be reopened every time a new rule is
found. The problem of adding rules leads to the main drawback of the
solution: extensibility. This solution is in no way extensible since
all the logic is congregated in the base class. This drawback does
pose problems, however with a little rejiggering the impact of this
drawback can be minimized. One possible fix, that will reduce the
extensibility problem, but will increase the work to create a
subclass, is to add an abstract method to the base that filters the
dictionary before it is sent out.&nbsp;</span></font></font>
</p> 
<pre class="prettyprint">abstract class Document
{
	IDictionary<string, string=""> FormTextFields
	{
		get
		{
			return new Dictionary<string, string="">
			{
				{ "NameField", this.NameField },
				{ "AgeField", this.AgeField }
			};
		}
	}
	
	protected abstract IDictionary<string,string> FilterTextFields(IDictionary<string,string> textFields);
}

class SmallDocument : Document
{
	protected override IDictionare<string, string=""> FilterTextFields(IDictionary<string,string> textFields)
	{
		textFields["NameField"] = SmallLogic();
		return textFields;
	}
	
	protected override DocumentType DocumentType
	{
		get
		{
			return DocumentType.Small;
		}
	}
}
</string,string></string,></string,string></string,string></string,></string,></pre>]]></description>
            <link>http://www.nearinfinity.com/blogs/sean_howell/modeling_multiple_logical_view.html</link>
            <guid>http://www.nearinfinity.com/blogs/sean_howell/modeling_multiple_logical_view.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">.NET</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Domain modeling</category>
            
            <pubDate>Sun, 20 Feb 2011 21:49:24 -0500</pubDate>
        </item>
        
        <item>
            <title>Micro Benchmarking with Caliper</title>
            <description><![CDATA[ From time to time I think all developers have done some form of benchmarking.  I recently discovered <a href="http://code.google.com/p/caliper/" target="new">Caliper</a> which is according to the site -  "Caliper is Google's open-source framework for writing, running and viewing the results of Java Microbenchmarks".  I am aware that micro-benchmarking can be misleading depending on who is writing the tests, but sometimes they are very helpful in getting a feel for how your code is running.
<h3>Background</h3>
Micro benchmarks are dead simple to do.  Get the current time in milliseconds, execute your code, then get the time in milliseconds again and subtract the difference.  So why use a tool like Caliper?  To me Caliper is great because it has a very familiar JUnit type of structure and feel to it. Instead of trying to describe what Caliper does, it's probably easiest to look at a simple example I put together.  I decided to benchmark implementations of Heap Sort,Merge Sort,Quick Sort and the sort method of the java.lang.Arrays class.  Not a terribly original idea I admit, but it was quick to do and it makes the point. <b>NOTE: Package statement and imports left out intentionally for brevity</b>
<pre class="prettyprint">
public class SortingBenchmarks extends SimpleBenchmark {
	
	private static final int SIZE = 100000;
	private static final int MAX_VALUE = 80000;
	private int[] values;

	@Override
	protected void setUp() throws Exception {
		values = new int[SIZE];
		Random generator = new Random();
		for (int i = 0; i < values.length; i++) {
			values[i] = generator.nextInt(MAX_VALUE);
		}
	}

	public void timeHeapSort(int reps) {
		for (int i = 0; i < reps; i++) {
			HeapSort.sort(values);
		}
	}

	public void timeMergeSort(int reps) {
		for (int i = 0; i < reps; i++) {
			MergeSort.sort(values);
		}
	}
	
	public void timeQuickSort(int reps) {
		for (int i = 0; i < reps; i++) {
			QuickSort.sort(values);
		}
	}

	public void timeArraysSort(int reps) {
		for (int i = 0; i < reps; i++) {
			Arrays.sort(values);
		}
	}
}
</pre>
<h3>Getting Started</h3>
Here are the basic steps to writing benchmarks in Caliper:
<ol>
<li>Extend the class SimpleBenchmark</li>
<li>Do any test setup/clean up in respective setUp or tearDown methods (Similar to JUnit setUp/tearDown)</li>
<li>Write the methods that will execute the code to benchmark starting with the word "time" (Again similar to JUnit, just "time" instead of "test")</li>
<li>Place the code you want to benchmark inside your timeSomeOperation methods</li>
</ol>
<h3>Getting and Running Caliper</h3>
To get started with Caliper
<ol>
<li>Go <a href="http://code.google.com/p/caliper/source/checkout" target="new">here</a> to get a read-only svn link to the source code</li>
<li>Check out the code then cd into &lt;CALIPER_INSTALL_DIR&gt; and run ant (obviously you need ant installed and on your path)</li>
</ol>
To actually run your benchmarks there is a bash script included in the project that you can use to run Caliper from the command line.  I took a little different approach as I wanted to run from inside my Eclipse project.
<ol>
<li>In &lt;CALIPER_INSTALL_DIR&gt;/build/caliper-0.0/lib/ there are two jar files, allocation.jar and caliper-0.0.jar.  These will need to be on the classpath of your project.  I chose to create a user library in Eclipse</li>
<li>JUnit will also need to be on the project classpath.  Again for me it's referenced in a user library</li>
<li>I created a driver class that simply executes the main method of the com.google.caliper.Runner class.  I pass in the name of my benchmark class by setting up a run configuration in Eclipse for my driver class</li>
</ol>
Here is the code for my driver class:
<pre class="prettyprint">
package bbejeck.caliper;

public class CaliperRunner {

    public static void main(String[] args) {
	com.google.caliper.Runner.main(args[0]);
    }

}
</pre>
After running the driver this is the output received in the Eclipse console screen:
<br/>
<pre style="font-size:medium">
0% Scenario{vm=java, trial=0, benchmark=HeapSort} 10889428.57 ns; ?=69810.62 ns @ 3 trials
25% Scenario{vm=java, trial=0, benchmark=MergeSort} 9066618.18 ns; ?=44341.70 ns @ 3 trials
50% Scenario{vm=java, trial=0, benchmark=QuickSort} 3312312.93 ns; ?=21028.91 ns @ 3 trials
75% Scenario{vm=java, trial=0, benchmark=ArraysSort} 3104668.79 ns; ?=23965.54 ns @ 3 trials

 benchmark    ms logarithmic runtime
  HeapSort 10.89 ==============================
 MergeSort  9.07 =========================
 QuickSort  3.31 ==
ArraysSort  3.10 =

vm: java
trial:
</pre>
<h3>Conclusion</h3>
Caliper is fairly new and is still has some work to be done, but I think that it's a great tool that could be very useful. Another potentially useful feature is that Caliper can <a href="http://code.google.com/p/caliper/wiki/OnlineResults" target="new">automatically publish your benchmarks.</a>   Full source code for my examples can be found on <a href="https://github.com/bbejeck/CaliperBlog" target="new">github</a>]]></description>
            <link>http://www.nearinfinity.com/blogs/bill_bejeck/mico_benchmarking_with_caliper.html</link>
            <guid>http://www.nearinfinity.com/blogs/bill_bejeck/mico_benchmarking_with_caliper.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Java</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">java</category>
            
            <pubDate>Tue, 01 Feb 2011 00:07:14 -0500</pubDate>
        </item>
        
        <item>
            <title>Mini Confluence Adds Diff/Change Viewing</title>
            <description><![CDATA[<p><img alt="Mini Confluence Diff View" src="http://www.nearinfinity.com/blogs/2010/12/10/diff.png" width="252" height="535" class="mt-image-right" style="float: right; margin: 0 0 20px 20px;" />
Last Thursday we published version 1.1 of <a href="http://www.miniconfluence.com">Mini Confluence</a>, which includes a new release of the Confluence plugin and iPhone client. The new plugin is available immediately, while the iPhone client will likely be published to the App Store within a week or two. But, you won't get any of the new features without updating the plugin, so do that now.</p>

<h2>View Diffs/Changes to Pages and News</h2>

<p>One really cool feature in this release is the ability to view historical changes of a page or news item. You'll now see a "View change..." link just below the author's name on any page or news item. Tapping that link will bring up the change view shown here.</p>

<p>Swiping left to right will take you to older versions of the content while swiping right to left will return you to newer versions. The view for each version shows what changed from the previous version. Additionally, tapping the View button at the top right will show you the rendered content of the specific version for which you're viewing the changes. It's pretty cool!</p>

<p>Please see the <a href="http://www.miniconfluence.com/blog/2010/12/09/mini_confluence_1.1_released.html">Mini Confluence 1.1 Release blog post</a> for full details on everything that changed in the new version.</p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/jeff_kunkle/mini_confluence_adds_diffchang.html</link>
            <guid>http://www.nearinfinity.com/blogs/jeff_kunkle/mini_confluence_adds_diffchang.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
            
            <pubDate>Mon, 13 Dec 2010 09:00:00 -0500</pubDate>
        </item>
        
        <item>
            <title>Clearance Track feelin&apos; the love in San Antonio</title>
            <description><![CDATA[<p>I'm writing today from the DSSS (Defense Special Security Systems) Conference in San Antonio. We're here showing Clearance Track, our product for security officers that helps organize and automate the clearance processes associated with getting and keeping your employees' clearances.</p>

<p>I was actually a little bit intimidated when we arrived and started setting up our booth yesterday -- our co-exhibitors rolled into the conference with crates taller than me full of degaussers (I had to google degausser), security doors, liquid scanners, extra large safes, alarm systems, and miscellaneous oversized, super-heavy equipment looking like it was taken from the set of a 1970s spy thriller.  </p>

<p>The truth is, we don't have 2,000 pounds of awesomeness at our booth. Not even if you throw in our pop-up display and our booth babes. Our product is software, so our coolest item is a 30" Apple monitor that we borrowed from our CEO's desk on which we display the software. But we're holding our own. We have a product that people <em>want</em>. One of our first visitors took one look at Clearance Track and said to my co-worker, "you're my new best friend."</p>

<p><img alt="CT-booth-babes.jpg" src="http://www.nearinfinity.com/blogs/CT-booth-babes.jpg" width="360" height="268" class="mt-image-left" style="float: left; margin: 0 20px 20px 0;" /></p>

<p>We thought we would come here and meet lots of security officers from small consulting companies like Near Infinity whose security officers were overwhelmed with processing clearances. But in talking to the attendees, we're finding more government folks charged with managing the clearance data for thousands of military or other government personnel. They use JPAS, but find it insufficient to report on and manage the details of their employees' clearances. They're getting by now with Excel spreadsheets, outdated Access databases, filing cabinets, and whatever info they can remember in their heads.</p>

<p>Clearance Track is a great solution for them. It provides a single, organized place to store all of their clearance data. It makes it easy to report on their secure personnel, easy to find documents related to that person's clearance, easy to pass off responsibilities from one security officer to another, easy to remember to renew a badge, easy to run tedious government reports, easy to auto-generate documents... Who doesn't need easy?</p>

<p>So far, the reaction has been great. Lots of people are raving about how helpful Clearance Track would be in their jobs, how user friendly it is, and how much time it would save them. With a day and a half left to go, we're not missing the heavy equipment too much.</p>

<p>And now, if you'll excuse me, I'd better get back to our booth visitors! </p>

<p><em>Clearance Track is a fast, secure way to manage your clearances. By providing a single place to track your investigations, crossovers, badge renewals, briefings, and certs, Clearance Track significantly streamlines the security officer's job and provides a collaborative environment to organize and store employee data. Use it for 4311s, 4414s, 312s, SF-86s, visit certs, annual security refresher briefings, and key government reports.</em></p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/caroline_wizeman/clearance_track_feelin_the_lov.html</link>
            <guid>http://www.nearinfinity.com/blogs/caroline_wizeman/clearance_track_feelin_the_lov.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Security</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Clearance Track</category>
            
            <pubDate>Tue, 30 Nov 2010 17:02:33 -0500</pubDate>
        </item>
        
        <item>
            <title>Conferences are Not For Learning</title>
            <description><![CDATA[<p>Near Infinity has the <a href="http://www.nearinfinity.com/home/currentopenings.html" title="Awesome Training Program">most generous training program</a> I've heard of among our competitors, and certainly rivals the best in the industry. Colleagues occasionally ask me for opinions about a conference or training course they're considering. My stock answer for such inquiries has always been to "attend a good training class for depth of understanding on a narrow topic and pick a conference for more broad but shallower knowledge of many areas." I think the training course advice is still accurate, but I was wrong about conferences.</p>

<p>The best conferences aren't about learning at all. They're about inspiration. I finally realized this at RailsConf last week, despite having attended dozens of conferences during the past ten years. Surprisingly, I found most of the topics rather boring, most of the presenters unpolished, and most of the presentations poorly constructed and minimally rehearsed. The things I enjoyed most were the keynotes from Dave Thomas and David Heinemeier Hansson, a performance-related presentation by Aaron Patterson, an entrepreneurial story by Tom Preston-Werner, and a reflection talk by Keavy McMinn on her journey from artist to programmer. Why did I enjoy them? Because they caused me to look at things differently, forced me to think critically about my choices, and motivated me to act. Only one of these was technical in nature by the way, just one! </p>

<p>A good conference should inspire you to do something. No talk is long enough to teach you anything immediately useful. The best you can hope for is exposure to lots of stuff you might want to look into after the conference. Of course you could get the same level of exposure by reading the conference agenda, Googling each topic, and reading through some documentation on each homepage. What you can't get is the inspiration from a great speaker, motivational story, or engaged community. </p>

<p>So the next time you contemplate attending a conference, do your best to judge the inspiration potential. After all, if you're not going to do anything different after the conference, why bother going?</p>
]]></description>
            <link>http://www.nearinfinity.com/blogs/jeff_kunkle/conferences_are_not_for_learni.html</link>
            <guid>http://www.nearinfinity.com/blogs/jeff_kunkle/conferences_are_not_for_learni.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Ruby</category>
            
            
            <pubDate>Mon, 15 Nov 2010 09:45:50 -0500</pubDate>
        </item>
        
        <item>
            <title>Launching Mini Confluence Enterprise Edition at the Atlassian Summit</title>
            <description><![CDATA[We're in San Francisco this week at the Atlassian Summit lunching Mini 
Confluence Enterprise Edition -- a way to access Confluence on your 
iPhone, Blackberry, Palm or Android. <br />
<br />At last year's summit, we released Mini Confluence Personal Edition,
 an iPhone app that individual users can purchase from the app store and
 run based on Confluence's XML-RPC. <br /><br />The new version is a custom 
plugin installed on the server. For people using an iPhone, they still 
download an app from the app store, but with the Enterprise Edition, 
it's free. For people with other mobile devices, they get to Mini 
Confluence through a web client. <br />
<br />This year's version is two times as fast as the original (!), and 
has some cool new features like filtering on the dashboard based on your
 favorites, status updates, landscape mode, and multiple user accounts. 
Find out more about Mini Confluence at <a href="http://www.miniconfluence.com/">www.miniconfluence.com</a>.<br /><u></u>
<br />We've been talking to lots of the conference attendees the past 
couple 
of days. Some of them have already been using the personal edition, and 
have given us feedback on that. Other people have ideas for MCEE, like 
an iPad version, support for Confluence instances protected by VPN, and 
even "make Mini Jira!"<br /><br />I'm surprised by how many people here are 
on Android. It's still mostly 
iPhones, but a lot less Blackberry users than last year. <br />
<br />
A lot 
of people have been signing up for the<a href="http://www.miniconfluence.com/get-it#beta"> beta program </a>-- we're sending out
 copies of the plugin for free to anyone who's interested and letting 
them try it out for three months. I'm anxious to hear the feedback so we
 can improve it before people start paying for it. <br />
<br />If you try it out, let us know what you think!<br /><br />And now... I 
think it's time for some Ghirardelli Chocolate :-) ]]></description>
            <link>http://www.nearinfinity.com/blogs/caroline_wizeman/launching_mini_confluence_ente.html</link>
            <guid>http://www.nearinfinity.com/blogs/caroline_wizeman/launching_mini_confluence_ente.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">iPhone</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Mini Confluence</category>
            
            <pubDate>Thu, 10 Jun 2010 18:24:31 -0500</pubDate>
        </item>
        
        <item>
            <title>Learning ANTLR part I</title>
            <description><![CDATA[<div style="font-size: 10pt;">This year one of my goals is to try and become proficient in using ANTLR.  I think that learning to translate text or build an external DSL is skill that, although not used everyday, will be very useful to know. For my first attempt I settled on something fairly easy, a SQL like grammar that could be used to search for files and the content within those files.  You should also be able to narrow the search results based on when the file was last modified.   My goal is to take something like the following:
<pre class="prettyprint">select * from /logs where file="*.out" and pattern="foobar" and modified &lt; 2 days ago
select * from /logs where file='*.out' and pattern='foobar' and modified between 20 and 30 minutes ago
</pre>
and translate it to the corresponding find command and pipe the results to xargs and grep:
<pre class="prettyprint">find /logs -name '*.out' -mtime -2 | xargs grep 'foobar'
find /logs -name '*.out' -mmin +20 -mmin -30 | xargs grep 'foobar'
</pre>
As an aside, if you are not familiar with xargs, check out <a href="http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/" target="new">this xargs tutorial</a> or the <a href="http://unixhelp.ed.ac.uk/CGI/man-cgi?xargs" target="new">xargs man pages</a> , it's a great utility that executes a command with the output of a previous command.
<h4>Disclaimer</h4>
Now before the villagers gather up with torches and pitch forks to run me out of town (I'm channeling <a href="http://en.wikipedia.org/wiki/Young_Frankenstein" target="new"> Young Frankenstein</a> here), I would like to make somewhat of a disclaimer.  I am not suggesting a new language or discouraging learning the *nix command line tools.  The point here is to learn ANTLR.  I found it more interesting to translate something I use everyday on my current project, versus some of the other "Hello World" ANTLR examples I have seen.  So other than a using this grammar as a learning exercise, I don't see it as being useful.
<h4>Introduction</h4>
ANTLR is a deep topic, so obviously one blog post can not go into any great detail.   So what follows is not in-depth coverage of ANTLR, but a detailed description of the grammar developed.  I will explain each section as well as  some of the decisions and trade-offs I made.  For my development environment I'm using:
<ol>
	<li>Eclipse 3.5.1</li>
	<li>Java 6</li>
	<li>The <a href="http://antlrv3ide.sourceforge.net/" target="new">ANTLR IDE </a>plugin for Eclipse.  You could also use <a href="http://www.antlr.org/works/index.html" target="new">ANTLRWorks</a>, the gui development environment for ANTLR.  ANTLRWorks is an excellent tool, I just felt more comfortable to do this work in Eclipse.</li>
	<li> ANTLR version 3.2</li>
	<li> Mac OS X 10.6.2.</li>
</ol>
So with all of that out of the way,  let's get started looking at the grammar.
<h3>options, @header</h3>
<pre class="prettyprint">grammar FQL;
options {
     language = Java;
}
@header {
     package bbejeck.antlr.fql;
}
</pre>
Here I am specifying a combined grammar named FQL.  (FQL is short for File Query Language and yes, I know the name sucks)
In options I'm specifying that I want the generated code to be  Java.  I could have also specified C,C++ or Python here as well.  ANTLR also has support for generating code in Ruby, but with the version I am using (v 3.2) I could not get it to work.  I did find <a href="http://rubyforge.org/projects/antlr3/" target="new">ANTLR Ruby</a>.  I have not tried it out, but from the documentation it looks promising.  The @header option is setting the package for the generated parser code.  This is also where I would have specified any needed imports.
<h3>@members</h3>

The @members section is where you place instance variables and methods that will be placed and used in the generated parser.  Most likely the code in the members section will be used in embedded actions in the parser rules.
<pre class="prettyprint"> @members {
  private StringBuilder findBuilder = new StringBuilder("find ");
  
  private StringBuilder filter = new StringBuilder();
  
  private void addString(String s){
    if(s!=null){
        findBuilder.append(s);
     }
  }
  
  private String buildTimeArg(String s, String snum, String sign){
       StringBuilder timeBuilder = new StringBuilder();
       int num = Integer.parseInt(snum);
       
       if(s.equals("days")){
           return timeBuilder.append(" -mtime ").append(sign).append(num).toString();
       }
       if(s.equals("hours")){
           return timeBuilder.append(" -mmin ").append(sign).append((num*60)).toString();
       }
       
       return timeBuilder.append(" -mmin ").append(sign).append(num).toString();
  }
  
  protected void mismatch(IntStream input, int ttype, BitSet follow) throws RecognitionException{
        throw new MismatchedTokenException(ttype,input);
  }
  
  public Object recoverFromMismatchedSet(IntStream input, RecognitionException e, BitSet follow) throws RecognitionException{
     throw e;
  }
  
}
</pre>
The two StringBuilders <i>findBuilder</i> and <i>filter</i> will be used by embedded actions to build up our translated query.   The reason for two StringBuilders will be explained when we cover the parsing rules.  The <i>addString</i> method is to check for optional tokens that could be null.  I could have easily checked for null in the embedded code within each rule,  but I felt it cluttered the grammar too much.  The <i>buildTimeArg</i> method is used as sort of a poor man's symbol table to translate the <i>modified</i> clause to the proper time format for the <i>mmin</i> or <i>mtime</i> arguments.  
The final two methods override how the generated parser responds to recognition errors (the generated parser extends ANTRL's Parser class which in turn extends the BaseRecognizer class).  By default ANTLR will recover from recognition errors and continue on, trying to read more tokens if available.   But in this grammar, if there is a recognition error along the way I want to stop processing right there.  

<h3>@rulecatch</h3>
Each parser rule is converted into a method call in the generated parser with a try - catch block surrounding the parsing code.  The catch statement here will be embedded in each one of the try-catch blocks in the parser.  
<pre class="prettyprint">@rulecatch{
    catch (RecognitionException e){
            throw e;
      }
}</pre>
If you remember from the previous section we want to stop parsing stop when RecognitionExceptions are encountered, so we re-throw the caught exception.
<h3>@lexer::header</h3>
Here we are specifying the package for the generated lexer.
<pre class="prettyprint">@lexer::header {
  package bbejeck.antlr.fql;
}
</pre>

Now let's move on to the parsing rules.

<h3>Parsing Rules</h3>
<pre class="prettyprint">evaluate returns [String query]
      :  query';' {$query = builder.toString() + filter.toString() ;}
      ;

query
       :   select_stmt where_stmt
       ;

select_stmt
      :  'select' '*' 'from' directory
      ;
</pre>
Here <i>evaluate</i> is our top level rule and returns a String, translated and built as the input is parsed.  Anything within the curly braces is code that will be embedded in the generated parser.  Note how we reference query from the grammar by placing a '$' before the word 'query'.  Also note that the string returned is a concatenation from the two StringBuilders we declared in the @members section.  The <i>query</i> rule is comprised of a <i>select_stmt</i> followed by a <i>where_stmt</i>.  The <i>select_stmt</i> is "select * from" followed by the directory rule.
<pre class="prettyprint">directory
       : (p='.'{addString($p.text);} | (p='/'?{addString($p.text);}IDENT{addString($IDENT.text);})+ )
       ;
</pre>
The directory rule accepts either a '.', a relative or an absolute path.  If the first expression is not provided there must be at least one path expression denoted by the '+'.  The variable 'p' is used to give a handle to the '.' or '/' token so it can be extracted . IDENT is a lexer rule which will be explained a little bit later.  All tokens here are passed into the <i>addString</i> method defined in the members section.
<pre class="prettyprint">where_stmt
       :  ('where'  clause ('and' clause)* ) ?
       ;
clause
       : file_name
       | pattern
       | modified
       ;
</pre>
The <i>where_stmt</i> rule expects the string 'where' followed by 0 or more clauses.  Also the entire <i>where_stmt</i>  is optional.  Here I chose form over substance.  By that I mean the grammar as it stands here will allow multiple clause's that would not make sense, i.e multiple file_name arguments etc.  I could have specified an exact order of clauses that would have also effectively set the limit of clauses entered, but I would rather the grammar be flexible and trust that the user knows what they want to do.
<pre class="prettyprint">  
file_name
       : 'file'  '=' STRING_LITERAL
         {addString(" -name ");addString($STRING_LITERAL.text);}
       ;

pattern
       :   'pattern'  '=' STRING_LITERAL
             { filter.append(" | xargs grep  ").append($STRING_LITERAL.text); }
       ;
</pre>
The <i>file_name</i> rule sets the -name argument again using the <i>addString</i> method.  The lexer rule STRING_LITERAL will accept whatever the user inputs.  The <i>pattern</i> rule builds up the grep command.  Here we see the use of the second StringBuilder <i>filter</i> that was defined in the @members section.  I feel that having a second StringBuilder to capture text for the grep filter is a hack.   The issue is that the <i>grep</i> command needs to be last in our translated query, but I really want the where statement to be in any order.  So by placing the tokens captured by the <i>pattern</i> rule in a separate StringBuilder I can easily guarantee the <i>grep</i> statement will be last.  
<pre class="prettyprint">modified
       :  modified_less
       |  modified_more
       |  modified_between
       ;
</pre>
The modified rule has three options.  This portion builds the mmin/mtime argument(s) for the <i>find</i> command.
<pre class="prettyprint">   
modified_less
       :   'modified'  '&lt;'  INTEGER time_span                             
           { addString(buildTimeArg($time_span.text,$INTEGER.text,"-")); }                     
       ; 
  
modified_more                     
       :   'modified'  '&gt;' INTEGER time_span
           { addString(buildTimeArg($time_span.text,$INTEGER.text,"+")); }
       ;

modified_between
       :   'modified' 'between' int1=INTEGER 'and' int2=INTEGER time_span
            { addString(buildTimeArg($time_span.text,$int1.text,"+")); }
            { addString(buildTimeArg($time_span.text,$int2.text,"-")); }
       ;
</pre>
The grammar allows you to specify searching by the time a file was last modified.  Here we use the method <i>buildTimeArg</i> to translate the input to the correct argument for either <i>mmin</i> (minutes modified) or <i>mtime</i> (days modified). Also take note of setting the two variables <i>int1</i> and <i>int2</i>.  Those are used to disambiguate which INTEGER token to use.
<pre class="prettyprint">time_span
       :   'days'
       |   'minutes'
       |   'hours'
       ;
</pre>
The time_span rule allows input of days, minutes or hours.  The hours argument is converted into minutes by the <i>buildTimeArg</i> method.

That's it for the parsing rules, now on to the lexer rules.
<h3>Lexer Rules</h3>
<pre class="prettyprint">fragment DIGIT : '0'..'9';
fragment LETTER : 'a'..'z'|'A'..'Z' ;

STRING_LITERAL : '\''.*'\'';
INTEGER : DIGIT+ ;
IDENT : LETTER(LETTER | DIGIT)* ;
WS : (' ' | '\t' | '\n' | '\r' | '\f')+  {$channel=HIDDEN;};
</pre>
DIGIT and LETTER are not lexer rules, as you can see by the fragment definition.  These are used for making the grammar more readable.  In the WS definition the {$channel=HIDDEN;} is used to ignore whitespace in the input.

<h3>Test Code</h3>
I used the following code to test the grammar from the command line:
<pre class="prettyprint">public class FQLTester {

public static void main(String[] args) throws Exception{
     BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
     String line = null;
     System.out.println("Enter your search:");
     while((line = reader.readLine())!= null){
         if(line.equalsIgnoreCase("quit")){
            System.exit(0);
         }
        CharStream charstream = new ANTLRStringStream(line);
        FQLLexer lexer = new FQLLexer(charstream);

        TokenStream tokenStream = new CommonTokenStream(lexer);
        FQLParser parser = new FQLParser(tokenStream);

        String parsed = null;
        try{
            parsed = parser.evaluate();
            System.out.println("parsed query is ["+parsed+"]");
            Process process = Runtime.getRuntime().exec(new String[]{"sh","-c",parsed});
            InputStream input = process.getInputStream();
            BufferedReader procReader = new BufferedReader(new InputStreamReader(input));
            String searchResults = null;
            while((searchResults=procReader.readLine())!=null){
                  System.out.println(searchResults);
            }
        }catch(Exception e){
               e.printStackTrace();
        }
      System.out.println("Enter your search:");
    }
}
</pre>

Since this blog is just scratching the surface as far as ANTLR's capabilities are concerned, I plan to be writing more about ANTLR in the near future.  Full source code for everything presented is <a href="http://github.com/bbejeck/antlr_code" target="new">available here</a>.
More resources for learning ANTLR are:
<ul>
	<li><a href="http://javadude.com/articles/antlr3xtut/index.html" target="new">Scott Stanchfield's video tutorial on ANTLR</a></li>
	<li><a href="http://www.pragprog.com/titles/tpantlr/the-definitive-antlr-reference" target="new">Definitive Guide to ANTLR, Pragmatic Books</a></li>
</ul>

That's it for now, thanks for your time.</div>]]></description>
            <link>http://www.nearinfinity.com/blogs/bill_bejeck/learning_antlr_part_i.html</link>
            <guid>http://www.nearinfinity.com/blogs/bill_bejeck/learning_antlr_part_i.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Java</category>
            
            
            <pubDate>Sat, 27 Feb 2010 00:44:19 -0500</pubDate>
        </item>
        
        <item>
            <title>Hanging out with industry leaders...</title>
            <description><![CDATA[Thanks to Near Infinity's generous training budget, I had the opportunity last week to spend 3 days with several members of the&nbsp;<a href="http://www.objectmentor.com/">Object Mentors</a>&nbsp;group. These guys have an enormous amount of experience, especially&nbsp;<a href="http://www.objectmentor.com/omTeam/martin_r.html">"Uncle" Bob Martin</a>. So I thought I would share a few of the pearls of wisdom they dropped along the way:<div><br /><div><ul><li>"Programming is a social exercise" - I thought this was a really good point. It was mentioned in the context of pair-programming, but I think it has far-reaching implications. Software development is more than just running through a bunch of formulas and crunching out an answer. Interaction within the team and with domain experts is crucial, to not only build the software right, but build the right software.</li><li>"A refactoring is something that takes a few seconds, or a few minutes at most" - I was really impressed with the importance of automated refactorings in his discussions. I think most of the time that I spend "refactoring" is in small, manual edits, whereas most of the time that he spends is in using automated refactoring to "chunk" his edits. I definitely need to learn these keystrokes and refactorings better. Maybe I should start a "Refactoring Driven Development" movement...</li><li>"Don't put refactoring on the schedule; do it all the time" - Simple, but effective. My tendency is to want to spend all my time refactoring, but this curbs that, because it forces me to refactor while I'm delivering user stories.&nbsp;</li><li>"There are 3 essential design skills: nose, vision, and plan" - A nose for recognizing design smells, a vision for seeing a good design for your codebase, and an ability to come up with a plan to get from point A to point B.&nbsp;</li><li>"Testing trumps good design" - This bothered me at first, but I think it's a really good point. The idea &nbsp;here is not to say that design is not important. But, rather, if you are forced to choose between between a "bad" design that allows better test coverage (e.g., less encapsulation), and "good" design which is hard to test, choose testability. The reason here is that the biggest roadblock to changing your codebase is not bad design, but FEAR of breaking something. If you know you will know when you've broken something, then you can retrofit a better design later.</li><li>"There is self-worth tied up in "finishing" something" - He also drew a distinction between having something working (which some developers will call "finishing" it), and finishing it - making it not only work, but be thoroughly tested, maintainable, etc.</li><li>Presentation layer - he talked about having the thinnest possible UI layer, which talks to a presentation layer to find out everything about how it should render. Then test the UI and business logic completely separately</li><li>"You aren't doing agile development unless you are tracking your velocity and remaining story points in terms of passing, automated acceptance tests" - I balked at this at first, feeling like it was too easy to use this as a performance to beat the team over the head with. After I thought about it, though, it's really about the definition of done. We are done if all the features we said would be working are working. And how can we know this? Only by testing them. Continuously. Which means it should be automated.</li><li>Acceptance tests don't need to be end-to-end, and, in fact, shouldn't be. This is another one that made me hesitate. After all, how do you know the feature is really working unless you go all the way from the UI to the database and back? Well, in short, because you're the developer. There's more value in being able to test features fast, constantly, than in being able to truly test them all the way from one end to the other. Mock/stub out what you need to to make that happen.</li><li>FitNesse is cool. This is the second time I've played around with that tool, and the second time I've been impressed with its power and simplicity. I definitely need to play around with it some more.</li></ul></div></div>]]></description>
            <link>http://www.nearinfinity.com/blogs/andrew_wagner/hanging_out_with_industry_lead.html</link>
            <guid>http://www.nearinfinity.com/blogs/andrew_wagner/hanging_out_with_industry_lead.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Agile Development</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Testing</category>
            
            
            <pubDate>Fri, 26 Feb 2010 09:50:17 -0500</pubDate>
        </item>
        
        <item>
            <title>Hello, World!</title>
            <description>I&apos;m a new software engineer here at Near Infinity, and very excited to be aboard! Hope to put some real content on this blog soon. </description>
            <link>http://www.nearinfinity.com/blogs/andrew_wagner/hello_world.html</link>
            <guid>http://www.nearinfinity.com/blogs/andrew_wagner/hello_world.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">meta</category>
            
            <pubDate>Sun, 31 Jan 2010 17:01:49 -0500</pubDate>
        </item>
        
        <item>
            <title>How to switch your Adobe Creative Suite from a PC to a Mac Platform</title>
            <description><![CDATA[I was considering switching from PC to a Mac, but I have Adobe CS4 Design Premium and didn't want to pay the $1,799 to buy it again. It was surprisingly difficult to find any information about whether or not I could do it, and if so how. Some Adobe employees at a trade show even told me that it couldn't be done.<br /><br />It turns out, though, that for $6.25 shipping and handling and the promise that you'll stop using and destroy your older version, they'll let you change. They call it Cross-Platform Swap. Here's how you do it:<br /><br /><ol><li>Go to <a href="http://www.adobe.com/go/supportportal">http://www.adobe.com/go/supportportal</a> If you have an account, log in. If you don't, you'll need to create one.<br /><br /></li><li>Under Get Support, Click on Orders and Returns<br /><br /></li><li>Choose the issue type Return / Exchange / Refund and then click the button that says Proceed to Online Form<br /><br /></li><li>Fill out the form, typing Cross Platform Swap in the subject line. Submit.<br />-- In the notes field, here's what I typed: <i>I am switching to a Mac and would like to do a cross-platform swap. I understand that my existing copy must be destroyed as soon as the new one arrives.</i><br /><br /></li><li>The next day, you'll get an email saying your issue is resolved and providing no useful information. Instructions for what to do next are actually in the ticket itself, which you can get back to on the support portal. Here's what you do:<br />-- Call 1-800-833-6687 and follow the prompts to customer service (#2 then #4)<br />-- Have your case number and credit card ready<br />-- Give them your credit card info and verify all your contact information<br /></li></ol>And that's it. They charge your credit card for $6.25 shipping and handling. Your old serial number gets invalidated, you destroy the old copy, and they send you the new one.<br /><br />As a side note, here's a tip for getting customer service from Adobe: Instead of going to the support portal when you have a question, go to the pages on their website where they have prices and information about purchasing products. Almost immediately, you'll get a helpful person IMing you asking if they can help. These IM helpers are nowhere to be found (ironically) in the support section of their site.<br /><br />Oh, and their customer support phone numbers aren't that easy to find, so here they are:<br /><b>Adobe Customer Support:</b> 1-800-585-0774 option #4<br />or<br /><b>Adobe Customer Support: </b>1-800-833-6687 option #2 then option #4.<br />&nbsp;<br />Good luck platform-swappers!<br /><br />]]></description>
            <link>http://www.nearinfinity.com/blogs/caroline_wizeman/how_to_switch_your_adobe_creat.html</link>
            <guid>http://www.nearinfinity.com/blogs/caroline_wizeman/how_to_switch_your_adobe_creat.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Adobe</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Illustrator</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">InDesign</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Mac</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">PC</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Photoshop</category>
            
            <pubDate>Fri, 18 Dec 2009 15:33:37 -0500</pubDate>
        </item>
        
        <item>
            <title>Installing Go on OSX Snow Leopard</title>
            <description><![CDATA[ Go is a new experimental language that has been released by Google that is suppose to have nearly the speed of C while executing, without a lot of the normal headaches of C/C++ coding.&nbsp; For example Go has garbage collection, dynamic types, reflections, etc. see <a href="http://www.infoq.com/news/2009/11/Go-Google-New-Language">Google Experiments With A New Language, Go</a> for a good overview.&nbsp; So after reading a few things about it, I wanted to try it out, and here is how I got it working on my 64-bit OSX machine.<br /><br />It's pretty basic, all I did was follow the instructions <a href="http://golang.org/doc/install.html">here</a>.<br /><br />First I added:<br /><br /><pre class="prettyprint">export GOROOT=$HOME/go<br />export GOOS=darwin<br />export GOARCH=amd64<br />export GOBIN=$HOME/gobin<br />export PATH=$PATH:$GOBIN</pre>
to ~/.bash_profile<br /><br />Run:<br /><br />
<pre class="prettyprint">. ~/.bash_profile</pre>
Next:<br /><br />
<pre class="prettyprint">sudo easy_install mercurial</pre>
This took a while for me to pull mercurial down, maybe their server was having problems. <br /><br />Note: I had to install the Xcode.mpkg from the Optional Installs on my Snow Leopard DVD to get gcc-4.3 installed.<br /><br />Next:<br /><br />

<pre class="prettyprint">hg clone -r release https://go.googlecode.com/hg/ $GOROOT</pre>
Next:<br /><br />
<pre class="prettyprint">cd $GOROOT/src<br />./all.bash</pre>
Note: One of the http tests goes out and touches google.com, I had to drop the firewall while building everything.&nbsp; After the build and tests were complete I turned it back on.<br /><br />And that's it! Time to make a hello world program.<br /><br />Create hello.go:<br /><br />
<pre class="prettyprint">package main<br /><br />import fmt "fmt"<br /><br />func main() {<br />&nbsp;&nbsp; fmt.Printf("hello world!\n");<br />}</pre>
Compile and Link:<br /><br />
<pre class="prettyprint">6g hello.go<br />6l hello.6</pre>
Run:<br /><br />
<pre class="prettyprint">./6.out</pre>
Output:<br /><br />
<pre class="prettyprint">hello world!</pre>
I am very excited about Go can't wait to write something real with it!<br /><br />]]></description>
            <link>http://www.nearinfinity.com/blogs/aaron_mccurry/installing_go_on_osx_snow_leop.html</link>
            <guid>http://www.nearinfinity.com/blogs/aaron_mccurry/installing_go_on_osx_snow_leop.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">c</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">go</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">google</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">language</category>
            
            <pubDate>Wed, 11 Nov 2009 14:52:23 -0500</pubDate>
        </item>
        
        <item>
            <title>Thinking Matters</title>
            <description><![CDATA[ <p>Aside from the fact that <a href="http://www.forbes.com/2009/04/29/java-oracle-sun-technology-internet-infrastructure-java.html">Oracle's Java Problem</a> contains all kinds of factual and other errors (see the comments on the post) this sentence caught my eye in particular when referring to Java being "quite hard to work with" - "Then, as now, you needed to be a highly trained programmer to make heads or tails of the language."</p>

<p>What's the issue here? That Java is hard to work with? Perhaps more specifically, not just Java but perhaps the artificial complexity in developing "Enterprise" applications in Java? Nope. The problem is that this type of thinking epitomizes the attitude that business people and other "professionals" tend to have about software development in general, in that they believe it is or should be easy and that it is always the tools and rogue programmers that are the problem. Thus, with more and better tools, they reason, there won't be a need for skilled developers and the monkey-work of actually programming could be done by, well, <a href="http://www.newtechusa.com/PPI/main.asp">monkeys</a>.</p>

<p>I believe software development is one of the hardest activities humans currently do, and yes I suppose I do have some bias since I am a developer. Also contrary to what many people think, there is both art and engineering involved, and any given problem can be solved in an almost infinite variety of ways. Unlike more established disciplines that have literally been around for hundreds or thousands of years (law, medicine, accounting, architecture, certain branches of engineering like civil, etc.), the software industry hasn't even reached the century mark yet! As a result there isn't any kind of consensus whatsoever about a completely standardized "body of knowledge" and thus there isn't an industy-recognized set of standard exams and boards like you find in the medical and law professions for example. (That topic is for a future post.)</p>

<p>One thing that is certain is that software development involves logic, and thus people who can solve problems using logic will always be needed, whether the primary medium stays in textual format (source code) or whether it evolves into some different representation like <a href="http://martinfowler.com/bliki/IntentionalSoftware.html">Intentional Software</a> is trying to do. So the statement from the article that "you needed to be a highly trained programmer to make heads or tails of the language" is always going to be true in software development. More generally, highly skilled people are needed in any complex endeavor, and attempts to dumb dumb complex things will likely not succeed in any area, not just software development. Would you trust someone to perform surgery on you so long as they have a "Dummies Guide to Surgery" book? Or someone to represent you in court who stayed at a Holiday Inn Express last night?</p>

<p>I hypothesize that things are becoming more complex as time moves on, not less. I also propose that unless we actually succeed in building <a href="http://www.scifi.com/battlestar/">Cylons</a> who end up wiping us all out or enslaving us, we will never reach a point where we don't need people to actually think and use logic to solve problems. So even though many business-types would love to be able to hire a bunch of monkeys and pay them $0.01 per day to develop software, those who actually realize that highly skilled people are an asset and help their bottom line, and treat them as such, are the ones who will come out on top, because they will smash their competitors who think of software/IT purely as a cost center and not a profit center.</p>]]></description>
            <link>http://www.nearinfinity.com/blogs/scott_leberknight/thinking_matters.html</link>
            <guid>http://www.nearinfinity.com/blogs/scott_leberknight/thinking_matters.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">General</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">Java</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">cylon</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">logic</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">programming</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">thinking</category>
            
            <pubDate>Thu, 30 Apr 2009 15:59:04 -0500</pubDate>
        </item>
        
    </channel>
</rss>

