


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

<channel>
	<title>CantRemembrances</title>
	<atom:link href="http://blog.cantremember.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.cantremember.com</link>
	<description>Memes of a technical vein discovered during CantRemember.com implementation</description>
	<lastBuildDate>Fri, 23 Mar 2012 06:03:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>delayedCallback(function(){ &#8230; }, delay);</title>
		<link>http://blog.cantremember.com/delayedcallbackfunction-delay/</link>
		<comments>http://blog.cantremember.com/delayedcallbackfunction-delay/#comments</comments>
		<pubDate>Fri, 23 Mar 2012 04:45:05 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=221</guid>
		<description><![CDATA[hola, Amigos! it&#8217;s been a long time since I rapped at ya! so, i&#8217;ve been doing plenty, i&#8217;m just not chatty about it. i built a Ruby migration framework using bundler, pry, spreadsheet, sequel and mp3info to build a JSON document version of my SEB Broadcast database. next up is some node.js to serve it [...]]]></description>
			<content:encoded><![CDATA[<p>hola, Amigos! it&#8217;s been a <em>long time</em> since <a href="http://www.theonion.com/search/?q=anchower">I rapped at ya</a>!</p>
<p>so, i&#8217;ve been doing plenty, i&#8217;m just not chatty about it. i built a Ruby migration framework using <a title="bundler" href="http://gembundler.com/">bundler</a>, <a title="pry" href="http://pry.github.com/">pry</a>, <a title="spreadsheet" href="http://rubyforge.org/projects/spreadsheet/">spreadsheet</a>, <a title="sequel" href="http://sequel.rubyforge.org/">sequel</a> and <a title="mp3info" href="http://ruby-mp3info.rubyforge.org/">mp3info</a> to build a JSON document version of my <a title="Listen to Sleepbot Environmental Broadcast" href="http://sleepbot.com/ambience/cgi/listen.cgi/listen.pls" target="_blank">SEB Broadcast</a> database. next up is some node.js to serve it up, then some <a title="RequireJS" href="http://requirejs.org/">RequireJS</a>, <a title="mustache" href="http://mustache.github.com/">mustache</a> (?) &amp; <a title="jQuery" href="http://jquery.com/">jQuery</a> goodness to spiff up the <a title="Sleepbot Environmental Broadcast" href="http://sleepbot.com/seb">SEB site</a></p>
<p>but in the meanwhile, i wrote this little gem at work:</p>
<div id="gist-2166671" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="c1">// returns a function that will invoke the callback &#39;delay&#39; ms after it is last called</span></div><div class='line' id='LC2'><span class="c1">//   eg. invoke the callback 500ms after the last time a key is pressed</span></div><div class='line' id='LC3'><span class="c1">// based on http://stackoverflow.com/questions/2219924/idiomatic-jquery-delayed-event-only-after-a-short-pause-in-typing-e-g-timew</span></div><div class='line' id='LC4'><span class="c1">//   but fixes the following:</span></div><div class='line' id='LC5'><span class="c1">//   (a) returns a unique timer scope on every call</span></div><div class='line' id='LC6'><span class="c1">//   (b) propagates context and arguments from the last call to the returned closure</span></div><div class='line' id='LC7'><span class="c1">//   and adds .cancel() and .fire() for additional callback control</span></div><div class='line' id='LC8'><span class="c1">// then of course, you could use underscore&#39;s _.debounce</span></div><div class='line' id='LC9'><span class="c1">//   it doesn&#39;t have the .cancel and .fire, but you may not care :)</span></div><div class='line' id='LC10'><span class="kd">function</span> <span class="nx">delayedCallback</span><span class="p">(</span><span class="nx">callback</span><span class="p">,</span> <span class="nx">delay</span><span class="p">)</span> <span class="p">{</span></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">callback</span><span class="p">,</span> <span class="nx">delay</span><span class="p">)</span> <span class="p">{</span>                          <span class="c1">// (2) receives the values in scope</span></div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kd">var</span> <span class="nx">timer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>                                           <span class="c1">// (3a) a scoped timer</span></div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kd">var</span> <span class="nx">context</span><span class="p">,</span> <span class="nx">args</span><span class="p">;</span>                                       <span class="c1">// (3b) scoped copies from the last invocation of the returned closure</span></div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kd">var</span> <span class="nx">cb</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">callback</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">context</span><span class="p">,</span> <span class="nx">args</span><span class="p">);</span> <span class="p">}</span>   <span class="c1">// (3c) called with proper context + arguments</span></div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="kd">var</span> <span class="nx">dcb</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>                                   <span class="c1">// (4) this closure is what gets returned from .delayedCallback</span></div><div class='line' id='LC16'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nx">context</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span></div><div class='line' id='LC17'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nx">args</span> <span class="o">=</span> <span class="nx">arguments</span><span class="p">;</span></div><div class='line' id='LC18'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">window</span><span class="p">.</span><span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span></div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nx">timer</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">setTimeout</span><span class="p">(</span><span class="nx">cb</span><span class="p">,</span> <span class="nx">delay</span><span class="p">);</span>                <span class="c1">// (5) only fires after this many ms of not re-invoking</span></div><div class='line' id='LC20'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">};</span></div><div class='line' id='LC21'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nx">dcb</span><span class="p">.</span><span class="nx">cancel</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nb">window</span><span class="p">.</span><span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span> <span class="p">};</span> <span class="c1">// (6a) you can cancel the delayed firing</span></div><div class='line' id='LC22'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="nx">dcb</span><span class="p">.</span><span class="nx">fire</span>  <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">cancel</span><span class="p">();</span> <span class="nx">cb</span><span class="p">();</span> <span class="p">};</span>         <span class="c1">// (6b) or force it to fire immediately</span></div><div class='line' id='LC23'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="nx">dcb</span><span class="p">;</span></div><div class='line' id='LC24'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">})(</span><span class="nx">callback</span><span class="p">,</span> <span class="nx">delay</span><span class="p">);</span>                                         <span class="c1">// (1) capture these values in scope</span></div><div class='line' id='LC25'><span class="p">}</span></div><div class='line' id='LC26'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2166671/a6287f2c9748a13518a945ea90c2db3d1271dd76/delayedCallback.js" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2166671#file_delayed_callback.js" style="float:right;margin-right:10px;color:#666">delayedCallback.js</a>
            <a href="https://gist.github.com/2166671">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>yes, i know.  so it turns out that i didn&#8217;t know about underscore&#8217;s <a href="http://documentcloud.github.com/underscore/#debounce" title="_.debounce()">_.debounce()</a> when i wrote it. eh. so much for DRY :)</p>
<p>still &#8212; i&#8217;m glad i thought it through. to me, this implementation captures the most powerful aspects of ECMAScript itself:</p>
<ul>
<li>scope-capturing closures</li>
<li>specifiable function context</li>
<li>freestyle properties on Object instances</li>
<li>single-threading (look ma, no <code>synchronize { ... }</code> !)</li>
</ul>
<p>anyway. bla dee blah. this post also gave me the incentive to start <a title="Embedded Gists" href="https://github.com/blog/122-embedded-gists">embedding gists</a> in my blog. nice helper widget, <a title="dflydev/embed-github-gist" href="https://github.com/dflydev/embed-github-gist">dflydev</a> !</p>
<p>peace out</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/delayedcallbackfunction-delay/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://sleepbot.com/ambience/cgi/listen.cgi/listen.pls" length="0" type="audio/x-scpls" />
		</item>
		<item>
		<title>weird sync issue = iPhone Notes will not die</title>
		<link>http://blog.cantremember.com/weird-sync-issue-iphone-notes-will-not-die/</link>
		<comments>http://blog.cantremember.com/weird-sync-issue-iphone-notes-will-not-die/#comments</comments>
		<pubDate>Sun, 07 Nov 2010 00:20:16 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=211</guid>
		<description><![CDATA[hola, amigos. s&#8217;up? i know it&#8217;s been a long time since I rapped at ya, but I&#8217;ve had a lot of stuff goin&#8217; down. i&#8217;ve had an issue with Notes on my iPhone. sure, i could use a content service like Evernote &#8212; which i did &#8212; but an even simpler tact for us Mac [...]]]></description>
			<content:encoded><![CDATA[<p>hola, amigos. s&#8217;up? i know it&#8217;s been a long time since <a href="http://www.theonion.com/search/?q=anchower">I rapped at ya</a>, but I&#8217;ve had a lot of stuff goin&#8217; down.</p>
<p>i&#8217;ve had an issue with Notes on my iPhone.  sure, i could use a content service like <a href="http://evernote.com">Evernote</a> &#8212; which i did &#8212; but an even simpler tact for us Mac folks is to keep a couple long-lived Notes around and just edit them.  lists n&#8217; shit.  you know the deal</p>
<p>anyway, so every once in a while Mail.app will have a sync conflict, and every once in a while i&#8217;ll click <code>[Sync Later]</code>, always by mistake.  now what i noticed is that there was a one-to-one (or nearly) relationship between the times i made that mistake, and Notes that would appear on my iPhone &#8230; that i <i>couldn&#8217;t delete</i></p>
<ul>
<li>i&#8217;d delete them, then they&#8217;d reappear after sync</li>
<li>there was no trace of them in Mail.app.  i explicitly flushed my Trash a couple times, but to no effect</li>
<li>you&#8217;d figure that the the mobile Notes would have singular authority &#038; ownership, but nope.  i&#8217;d delete them, they&#8217;d come back, and they kept getting older and more annoying</li>
<li>the friendly Genius Bar staff had no other advice to give besides doing an explicit iTunes re-sync via <code>Advanced</code> | <code>Replace information on this iPhone</code> | <code>[x] Notes</code>, but to no effect</li>
</ul>
<p>now, i know that this isn&#8217;t a particularly <i>technical</i> post.  however since i couldn&#8217;t find any reference to anyone else having this problem &#8212; at least based on the keywords i was using &#8212; i figured i&#8217;d drop a few of them and describe the solution:</p>
<ul>
<li>view the Note, select all, and delete</li>
<li>the mobile app auto-deletes the empty Note</li>
<li><b>voila!</b>.  it&#8217;s gone for good</li>
</ul>
<p>there.  now you know what i know :)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/weird-sync-issue-iphone-notes-will-not-die/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Upgrading your Rails Development Mac to Snow Leopard</title>
		<link>http://blog.cantremember.com/upgrading-your-rails-development-mac-to-snow-leopard/</link>
		<comments>http://blog.cantremember.com/upgrading-your-rails-development-mac-to-snow-leopard/#comments</comments>
		<pubDate>Sun, 07 Feb 2010 06:17:12 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Learning]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=196</guid>
		<description><![CDATA[Oh, there is such joy in the process of upgrading to Mac OS/X Snow Leopard for us developer folks. Me, I chose the in-place ugrade path &#8230; my co-worker, who chose the from-scratch path, was deprived of some of these pleasures. Then again, he had to reconstruct everything from scrach, so he had his own [...]]]></description>
			<content:encoded><![CDATA[<p>Oh, there is such <i>joy</i> in the process of upgrading to Mac OS/X Snow Leopard for us developer folks. Me, I chose the in-place ugrade path &#8230; my co-worker, who chose the from-scratch path, was deprived of some of these pleasures. Then again, he had to reconstruct everything from scrach, so he had his own barrel of monkeys to contend with.</p>
<p>Here&#8217;s all the bumps I ran into, pretty much in reverse order as I tried (unsuccessfully) to do the minimum amount of work possible :) I had to go pretty much this entire process twice &#8212; once on my work Macbook Pro, once on my identical home verison &#8212; so I figured I might as well document all of this crap down in the hope that it may reduce the shock and awe of future migrators. Of course you may run into a mess of fun issues not described here &#8230; And pardon me if the instructions aren&#8217;t <i>perfect</i>, because I&#8217;ve tried to boil a lot of this down to the <b>it-finally-worked</b> steps extracted from the frenzied back-and-forth of my real-time upgrade experience :)</p>
<h3>Backups</h3>
<p>Yes, this is just <i>common-sense developer stuff</i> (as is a number of the other obvious things that I call out in this post). </p>
<p>You&#8217;ll probably want to do a full <code>mysqldump</code> before upgrading. You can dump your <code>port install</code> and <code>gem list --local</code> listings up front as well, or wait &#8217;til you get to those respective steps below.</p>
<h3>MacPorts</h3>
<p>If you also chose the <b>MacPorts</b> library system, you&#8217;ll need to re-install it from scratch. You&#8217;ll need <b>X11</b> from the Snow Leopard from the OS/X install disks, and download the latest version of <a href="http://www.apple.com/macosx/developers/#xcode"><b>Xcode</b></a>. Follow the migration steps <a href="http://trac.macports.org/wiki/Migration">as outlined on their Wiki</a>; it does the trick. </p>
<p>Save off your <code>port install</code> list as a reference. Now, your MacPorts install will be completely toast, so that command won&#8217;t work until you re-install. No problem though &#8212; all of your packages will still be listed even after you upgrade. </p>
<p>The <code>port clean</code> step in the Wiki will crap out in the <code>http*</code> range, but that&#8217;s fine &#8230; you can probably skip that step anyway. Re-install your core packages and you&#8217;re good to go. I suggest installing <code>readline</code> if you haven&#8217;t, because it&#8217;s very useful in <code>irb</code> or any Ruby console.</p>
<h3>MySQL</h3>
<p>It was not necesary for me to build MySQL from source. Instead, I just installed <a href="http://dev.mysql.com/downloads/mysql/">the x86_64 version of <b>MySQL</b></a> &#8212; the latest was <code>mysql-5.1.42-osx10.6-x86_64.dmg</code> af the time of this writing.</p>
<p>If this is a 5.x verison upgrade for you as well, the install will just re-symlink <code>/usr/local/mysql</code>, so your old data will still be in your previous install dir. </p>
<p>I didn&#8217;t make <b>mysqldump</b>s before I did the upgrade <i>(handpalm)</i> so I had to copy over my <code>data/</code> files raw and hope that the new version would make friends with them. Initially I had problems with InnoDB.  It wasn&#8217;t showing up under <code>show engines;</code>, and when I tried to manually install the plug-in &#8212; per <a href="http://bugs.mysql.com/bug.php?id=48787">this bug report</a>, which explains the whole thing &#8212; it would fail on the  &#8216;Plugin initialization function&#8217;. Turns out you need to do <b>two</b> things when you bring over raw data:</p>
<ul>
<li>Whack your <code>/var/log/mysql/*binary*</code> / binary log files in order to get past <code>mysqld</code> startup errors.</li>
<li>Whack your <code>ib_logfile*</code> files too. Once you do that, there&#8217;s a good chance MySQL will regen them in recovery mode. Me, I had no choice (except rolling back with Time Machine). Miracle of miracles &#8230; <i>it works!</i></li>
</ul>
<p>Don&#8217;t try this at home kids.  Make your backups.  Note: here&#8217;s the correct link to the <a href="http://dev.mysql.com/doc/refman/5.1/en/adding-and-removing.html">manual page on InnoDB tablespace fun</a>.</p>
<h3>x86_64 ARCHFLAGS</h3>
<p>Snow Leopard is a lot more native 64-bit than previous OS/X versions, and when you do your manual builds &#038; makes, you may want to set the following <b>environment variable</b>:</p>
<div class="pre_wrap">
<pre><code>export ARCHFLAGS="-Os -arch x86_64 -fno-common"
</code></pre>
</div>
<p>You&#8217;ll see a set of similar (though mixed) recommendations in the blogs I reference below; this particular flagset worked for me.</p>
<h3>Ruby</h3>
<p>I built <b>Ruby 1.9</b> at work, and <b>1.8.7</b> on my personal machine.  Either path is fine, just <a href="http://www.ruby-lang.org/en/downloads/">pick up the latest source</a> of your choosing. <a href="http://cho.hapgoods.com/wordpress/?p=158">Chris Cruft&#8217;s blog post</a> goes into some of the details I&#8217;m describing here as well. Basically, the README boils down to:</p>
<div class="pre_wrap">
<pre><code>autoconf
./configure --with-readline-dir=/usr/local
make clean &#038;&#038; make
make install
</code></pre>
</div>
<p>Though there&#8217;s no reason in the world that you&#8217;d want to &#8212; it has been superceded &#8212; do <i>not</i> install <b>ruby 1.9.1p243</b>. If you do, you&#8217;ll never get the <code>mysql</code> gem to work. Or wait, or was it <code>mongrel</code>? Well, it was one or the other &#8230; just trust me, it&#8217;s <i>bad</i>.</p>
<h3>Gem</h3>
<p>I re-built <a href="http://rubyforge.org/frs/?group_id=126"><b>gem</b> from source</a> from scratch as well, just to be sure. Save off your <code>gem env</code> and <code>gem list --local</code> as a reference. And before you start installing gems, you&#8217;ll also probably want to make sure you&#8217;re fully up-to-date with <code>gem update --system</code>, though that&#8217;s probably redundant.</p>
<p>Uninstall and re-install all of your gems; if some won&#8217;t uninstall even though they&#8217;re listed, it may be an install path issue. Use <code>gem list -d GEMNAME</code> to find where your gem was installed, and then use <code>gem uninstall -i DIRNAME GEMNAME</code> to finish the job. </p>
<p>With the ARCHFLAGS in place, the vast majority of the native source gem builds will go smoothly, but there are some notable exceptions &#8230;</p>
<h3>The mysql Gem</h3>
<p>Uh huh, this is the one gem that gets me every time. And again, you <i>won&#8217;t</i> have needed to have built MySQL from source. </p>
<p>For starters, you may way want to glance over this very useful <a href="http://www.icoretech.org/2009/08/install-mysql-and-mysql-ruby-gem-on-snow-leopard-64-bit/">iCoreTech blog post</a> to see if it works for you. But if you run into a lot of issues like I did, you may need to do it in two steps:</p>
<h4>Fetch and Install the Gem</h4>
<p>At the time of this writing, either <b>mysql</b> gem version <code>2.7</code> or <code>2.8.1</code> will do the trick.</p>
<div class="pre_wrap">
<pre><code>gem fetch mysql --version VERSION
gem install mysql -- --with-mysql-dir=/usr/local --with-mysql-config=/usr/local/mysql/bin/mysql_config
gem unpack mysql
</code></pre>
</div>
<p>Sadly, it may fail, either during the build or when you try to test it. I was able to successfuly run the (included?) <code>test.rb</code> at my workplace, but as simple as that sounds, I swear <i>I don&#8217;t remember how I did it</i> ! The second time, at home, I only found the problems retroactively when I tried to get my Rails projects to boot. If you do find and run the <code>test.rb</code>, you&#8217;ll need to make sure that the standard MySQL <code>test</code> database exists. </p>
<p>Both times, one of the big blockers that I &#8212; and many other people &#8212; ran into was:</p>
<div class="pre_wrap">
<pre><code>NameError: uninitialized constant MysqlCompat::MysqlRes
</code></pre>
</div>
<p>If so, try this:</p>
<h4>Manually Re-build the Binary</h4>
<p>Go into <code>ext/mysql_api</code>, make sure your <b>ARCHFLAGS</b> are exported as described above, and &#8230;</p>
<div class="pre_wrap">
<pre><code>ruby extconf.rb --with-mysql-config=/usr/local/mysql/bin/mysql_config
make clean &#038;&#038; make
make install
</code></pre>
</div>
<p>Hopefully your newly built &#038; installed binaries will resolve the issue.</p>
<h3>Mongrel</h3>
<p>It took me a little effort to build <a href="http://mongrel.rubyforge.org/"><b>mongrel</b></a> on Ruby 1.9 with the x86_64 architecture. My memory is a little hazy &#8212; since my 1.8.7 build at home worked perfectly through standard <code>gem install</code> &#8212; but buried deep in this <a href="http://isitruby19.com/mongrel">Is It Ruby contributory blog post</a> are probably all the answers you&#8217;ll need.</p>
<p>Under Ruby 1.9, I <i>did</i> had to modify the source, which (to paraphrase) involved some global code replacements:</p>
<ul>
<li><code>RSTRING(foo)->len</code> with <code>RSTRING_LEN(foo)</code></li>
<li><code>RSTRING(foo)->ptr</code> with <code>RSTRING_PTR(foo)</code></il>
<li>change the one-line <code>case ... when</code> statements from Java-esque <code>:</code> delimiters to <code>then</code>&#8216;s.
</ul>
<p>And then the re-build:</p>
<div class="pre_wrap">
<pre><code>ruby extconf.rb install mongrel
make
make install
cd ../..
ruby setup.rb
gem build mongrel.gemspec
gem install mongrel.gem
</code></pre>
</div>
<h3>Conclusion</h3>
<p>And that&#8217;s as far as I had to go. <i>Whew!</i> I certainly hope that my post has been of some assistance to you (and with a minimum of unintended mis-direction). Of course, I learned everything the I reiterated above by searching the &#8216;net and plugging away. And there&#8217;s plenty of other folks who&#8217;ve gone down this insane path as well. Good luck, brave soul!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/upgrading-your-rails-development-mac-to-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>What Would a Wookie Do?</title>
		<link>http://blog.cantremember.com/what-would-a-wookie-do/</link>
		<comments>http://blog.cantremember.com/what-would-a-wookie-do/#comments</comments>
		<pubDate>Sat, 12 Sep 2009 20:05:56 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[bot]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=173</guid>
		<description><![CDATA[Yes, I had to ask myself that question a lot recently, at least from the perspective of how he would use the Twitter service. As much as this is a theoretical question, I believe I came up with some answers, and they are made manifest in the @cr_wookie Personality Engine. Base-line Aesthetics The first step [...]]]></description>
			<content:encoded><![CDATA[<p>Yes, I had to ask myself that question a lot recently, at least from the perspective of how he would use the <a href="http://twitter.com/cr_wookie">Twitter service</a>.  As much as this is a theoretical question, I believe I came up with some answers, and they are made manifest in the <a href="http://wiki.cantremember.com/Twitter/Wookie"><strong>@cr_wookie Personality Engine</strong></a>.</p>
<h3>Base-line Aesthetics</h3>
<p>The first step in bringing a Wookie to life was to establish a basic <strong>phoenetic dialect</strong>. So I came up with a set of candidates &#8216;words&#8217; along the lines of; <em>auhrn</em>, <em>rghrn</em>, <em>gahrn</em>, <em>hraur</em>, <em>urau</em>, <em>ehruh</em>, and <em>nrauh</em>. Hey, they sounded good. There&#8217;s about 60 of them total, comprised of the letters A E G H N R and U. After having watched <a href="http://starwars.wikia.com/wiki/Empire_Strikes_Back">The Empire Strikes Back</a>, where Chewbacca seems to get most of his good lines, I expanded into a few W words as well (<em>wahr</em> seems to work particularly well). <a href="http://www.mcsweeneys.net/links/lists/chewbacca.html">Many folks postulate</a> that he was capable of speaking Os and Ks, yet I myself do not subscribe to that opinion.</p>
<p>I then used <a href="http://rubyforge.org/projects/madderlib/">MadderLib</a> to construct simple word generators which allowed the phoenemes to stretch appropriately for different <strong>word lengths</strong>. Long vowels, double Rs and Hs, whatever looked good. And with a little compositing logic, I came up with some sentence patterns that were quite fun to read out loud:</p>
<blockquote><p>
rauhr nruuuhh raghr uhr rghrrnnauhrnaauuuuuurhhh<br />
nrauuh euu gauuhrr ruhr<br />
urhn ehrraaah rhhreuhraahhrrrrnn gaurrh
</p></blockquote>
<p>Those are rather plain-looking though. In order to make the Wookie&#8217;s statements seem more like <strong>txt argot</strong>, some variety was needed. Punctuation was obvious, both terminating and delimiting (commas, semicolons). Plus there&#8217;s the proper use of txt idioms, the LOLs and WTFs that are so popular with the youngsters these days (AFAIK). Throw in a nice little collection of emoticons, and behold; the Wookie has charm:</p>
<blockquote><p>
LOLZ! uhrrn euueuuhhaur, eruh hraur nrauururhnehrrraaah auhrn harn aurreruhaaarruuuhh rraaghrrr ^_^<br />
hraur rghneruh nuh waahhr???<br />
rhhhnnghn uhrrrnn ehrah. euu urau ehuuurrr urrn aurheuuuh haarn uhrrrn k?&#8230; haauurrr nruharuhuhhrrh hruaaaauuh ghrn rghn nrruuhh
</p></blockquote>
<p>Well, yeah, they still look sorta flat. Real people quote and capitalize portions of what they type, and there are other <strong>non-verbal components</strong> to the average sentence. So, the Wookie was taught to inject numbers, times, abbreviations, and even Star Wars calendar years into his sentences:</p>
<blockquote><p>
gahrn rghhr ghrrnehur rghhrrn hruauh raghrehurauuuuuuhrn gahrn?? hrraaauu: hrrraaauu rauhr ;) _ehruuh_ &#8216;Rahr Ehrrraaaah&#8217;<br />
raauh hrau Ehrahurrrrh *rauhr* gruh<br />
auhrneuuhr. harnuhrrnuhrrr uhr &#8211; raauhneuuu 1:30 gahrn raauuughrrr *nuuh* aahhrruuuunn uhr. wuurh harn rhr?
</p></blockquote>
<p>Once the Wookie was at this point, he could talk for quite some time and produce diverse aesthetic results. Reading them out loud is a <em>hoot</em>! Thus was born the first Personality Engine bot (the Wookie is comprised of four of them). But he still wasn&#8217;t really tweeting until he could follow some of the <strong>core Twitter memes</strong>.</p>
<h3>Twitter Memes</h3>
<p><a href="http://hashtags.org"><strong>Hash tags</strong></a> were the first obvious choice, since they were easy to fake. To this day, the Wookie can simply prepend a # to any word or composite that he speaks. But to make this feature really zing, I added support for Twitter&#8217;s <em>trending searches</em>. This allowed him to use real-world releveant tags, injecting them into his sentences, or appending them to his tweet (as is common convention). It turns out that one of the joys of a nonsense grammar is that anything which isn&#8217;t nonsense magically becomes the <em>&#8216;meaning&#8217;</em> of the sentence:</p>
<blockquote><p>
Harn ehureruheuuu &#038; ahrn rrghhhn! AAAHHRRNNAUHRN EHUUR! #itsnotgonnawork<br />
WAHR NUUUH RAUHRR!!! euu &#8216;aauurr nraur&#8217; haaauurrurau! #fact<br />
GRRUUUH! uhr hraheuuuuhhr aauurh #ChargersSuck rrhnn aur! gauhr aaurh haurerruuuhh! !! #aurh
</p></blockquote>
<p>Of course, no Twitter user can resist posting <strong>shortened URLs</strong>. They&#8217;ve been a cornerstone to the explosive growth of the service, maybe because there&#8217;s just so much interesting fast-moving crap out there on teh Internets. The Wookie follows several aggregation services &#8212; <a href="http://feeds.digg.com/digg/popular.rss">Digg</a>, <a href="http://feeds.technorati.com/">Technorati</a> &#8212; and a smattering of other popular blogs &#8212; <a href="http://www.tmz.com/rss.xml">TMZ</a>, <a href="http://feeds.theonion.com/theonion/daily">The Onion Daily</a>, <a href="http://feeds.feedburner.com/ICanHasCheezburger">LOLcats</a> &#8212; etc. He pulls out links to recent content and shortens them with the <a href="http://bit.ly">bit.ly</a> API. Again, since the Wookie is totally faking it, the results just cannot be accounted for. The best he can hope for is that the emotional texture of his tweet sometimes support the referenced source:</p>
<blockquote><p>
nrauh rrhn hrauuur ehrruhauhrnurrh. nrraauuuhh IMHO. hraur grraauhurhnauhrn rauh http://bit.ly/mjOD7<br />
Ghrn euuhrr? haarneruuuhh urrn aruh rghrnn aaur, uhr hruh urrr :) http://bit.ly/1a9j1K
</p></blockquote>
<p>And no tweeter lives in a vacuum; their posts are replete with the <strong>user handles</strong> of friends, comrades and mentors.<br />
The Wookie wasn&#8217;t about to make up handles, so his likely choices were his followees and followers.<br />
Rather than take the name-dropping approach here &#8212; more on that later &#8212; the Wookie chooses to occasionally reference his most recent followers:</p>
<blockquote><p>
OMG! _rrrhnnneuh_ euh: urr wuuurrh rghnurrnh urhn hruhn @sleepbotzz rhagn ghrn rrhn waaahhr hruauhehuraghrrrrrnnn rhagn harrnaurh </p></blockquote>
<p>After these features were implemented, the Wookie&#8217;s posts started to look almost real-ish. And whenever he tweets on his own, that is his range of capabilities. But he&#8217;s still not a real member of the Twitter community until he could play some other tricks. Thus began a completely separate effort; how to <strong>translate</strong> English into Wookie.</p>
<h3>Mocking</h3>
<p>Did I say &#8216;translate&#8217;? What I meant to say was &#8216;<strong>mock</strong>&#8216;.  After all, what can you really do with a nonsense grammar except make it <em>look</em> like it has meaning.</p>
<p>So, the Wookie was taught to mock existing sentences into his own dialect. He simply matches the initial letter (vowel / not) and preserves the word&#8217;s length and non-alpha characters (for contractions and the like). Special mappings were also added to deal with short words (the dialect only generates words 4-letters and above). And within a given tweet, he re-uses the same fake word for each instance of the real one. It&#8217;s an obscure feature, but it makes a <em>helluva</em> difference in some specific cases.</p>
<p>The totally awesome part of effort is identifying the words that <em>don&#8217;t get mocked</em>. There was no way I wanted to deal with semantic grammar detection, since tweets are often wildly non-grammatical. So as per usual, the Wookie fakes it. It mainly comes down to a weak analysis of <strong>quoting and capitalization</strong> patterns. He also keeps hash tags, links, handles, many acronyms, and argot &#8212; to the best of his ability.</p>
<p>And just for fun, he also recognizes a rather large <strong>lexicon of terms</strong> from the Star Wars universe. Well, except for the term &#8216;Star Wars&#8217; that is. He doesn&#8217;t know what that means.</p>
<p>It took a lot of experimenting to get it right, and he still makes mistakes, but he&#8217;s getting smarter all the time. One of the interesting things I learned during development was how staccato the English language is, as compared to the long smooth yawls of Wookie. Reading back a mocked sentence out loud is a sublime experience.</p>
<p>You may ask, how can this awesome power best be used to serve the Good?</p>
<h3>Re-Tweeting</h3>
<p>Darn right the Wookie <strong>re-tweets</strong>! He simply selects a few users that he follows, derives their recent tweets, and mocks one of them up. There are some users &#8212; <a href="http://twitter.com/darthvader">@darthvader</a> for instance &#8212; which he will always re-tweet if the user has posted anything fresh. Otherwise it&#8217;s a simple random selection, after avoiding repeats (there&#8217;s extensive repeat-avoiding code all throughout the Wookie implementation). There is the slight hint of name-dropping here, since he tends to follow a lot of popular accounts, but that&#8217;s just the nature of this beast.</p>
<blockquote><p>
RT @warrenellis Rauuh&#8217;r @neilhimself ehr #neilfail ar hruun rraaauuuuhrrr? Au. #warrenfail. http://bit.ly/16IiUE<br />
RT @KurzweilAINews: First Close Look At Stimulated Brain: Aghhrrrrnn gaauuuhhrrr ar hrrauur aauuuhrrn u gahrrn &#8230; http://bit.ly/17dg34<br />
RT @cnnbrk Hruh. Urrnn nrrauuuhh Ted Kennedy ur &#8220;rrrhhrrr ghr rauhn hru rau wahr; wahr au Democratic Party; &#8230; http://bit.ly/3FmLyq
</p></blockquote>
<p>It turns out that injecting the re-tweet &#8216;header&#8217; will often push longer ones past the 140ch barrier. He will attempt to preserve as much of the original tweet as possible, focusing on trailing URLs and hash tags. And if the tweet is short enough, he posts a shortened link to the original post, primarily to show off his mad skills. He is much inclined towards tweets which have a good blend of mockable and preservable words, again, to show off his mad skills.</p>
<p>This became the second Personality Engine bot. Yet still, re-tweeting is a one-way street, and <strong>interaction</strong> is the real key to user engagement.</p>
<h3>Playing Well With Others</h3>
<p>The third Personality Engine bot was borne of the need to perpetuate the following cycle of fun. On a regular basis, the Wookie will <strong>search for references</strong> to relevant words &#8212; <em>wookiee</em>, <em>kashyyyk</em>, etc. &#8212; and will respond to the user with a generated tweet. This is much less invasive and cruel than auto-following, a botting practice which I find to be quite <a href="http://dictionary.reference.com/browse/gauche">gauche</a>. I can only imagine the surprise on these user&#8217;s faces:</p>
<blockquote><p>
@amynicole21 WTF! aruh nrauuhehruhaaauuurr rraaahhhrr nruuh urrrrn ghrn wurh: euu haarrn nuuh grruhuhrnuhhrrn erruuuuhh :)<br />
@DZ1641 gauuuhrr??<br />
@vfigueroa1 rghrurr waahr! rrhneuuuhr urr nruuh hrauh &#8211; *ehrraaah* nrauh ^_^ nraur hruun rrrghhnn
</p></blockquote>
<p>However, before he goes searching, he first looks at his <strong>recent mention history</strong>, specifically at tweets starting with <em>@cr_wookie</em>. If one is found, he will mock and publicly respond to it, linking back to the original post when length permits. So if you talk to the Wookie, there&#8217;s a reasonable chance that he&#8217;ll republish you. To minimize abuse of this feature, he doesn&#8217;t follow quite the same word preservation rules as he does for follower re-tweeting. But he&#8217;ll keep Star Wars words, and that opens up a <em>vast</em> realm of potential amusement.</p>
<blockquote><p>
. @kindadodgy Nurh U hraaauuuu rghn Wookie rauuhrr wau&#8217;r hruh nrh ghrn hrun &#8216;rhngn ruhrn uhr wurh rn nuuh gh, ahrn au nrruuuuhh gauurh.<br />
> @adamlampert Ar. U hru nrh raaghrr gh HR&#8217;N! Rghn ruuhr au! http://bit.ly/591qW<br />
.@Lillput Nrh, rhag&#8217;n rauuhr nurh ghr haurr au a rghhnn ur a rghn.
</p></blockquote>
<h3>Greeting New Followers</h3>
<p>The fourth and final Personality Engine bot is the <strong>greeter</strong>. When you follow him, he&#8217;ll DM you. Short and to the point.</p>
<h3>Summary</h3>
<p>Whew! All in all, the project required about 6 weeks of spare time. My only hope is that much hilarity will ensue from these efforts.</p>
<p>If you want to read a bit more about the Wookie &#8212; and who wouldn&#8217;t, right? &#8212; you can check out his <a href="http://wiki.cantremember.com/Twitter/Wookie"><strong>Wiki page</strong></a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/what-would-a-wookie-do/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Snake &#8216;n&#8217; Bacon in The DDOS Caper!</title>
		<link>http://blog.cantremember.com/snake-n-bacon-in-the-ddos-caper/</link>
		<comments>http://blog.cantremember.com/snake-n-bacon-in-the-ddos-caper/#comments</comments>
		<pubDate>Sat, 08 Aug 2009 04:52:48 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[bot]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=168</guid>
		<description><![CDATA[ah, come in! we&#8217;re so glad you&#8217;ve come Snake &#8216;n&#8217; Bacon! i&#8217;m crisp delicious bacon sssss glad you asked. it seems there&#8217;s a group of hackers, and we want you to go in under-cover i go great on a sandwich sssss &#8230; When Twitter came back online yesterday afternoon after their networking attacks, I got [...]]]></description>
			<content:encoded><![CDATA[<dl>
<dt>
<dd><i><br />
ah, come in!  we&#8217;re so glad you&#8217;ve come <a href="http://twitter.com/cr_snake_bacon">Snake &#8216;n&#8217; Bacon</a>!<br />
i&#8217;m crisp delicious bacon<br />
sssss</p>
<p>glad you asked.  it seems there&#8217;s a group of hackers, and we want you to go in under-cover<br />
i go great on a sandwich<br />
sssss</p>
<p>&#8230;<br />
</i></dd>
</dt>
</dl>
<p>When Twitter came back online yesterday afternoon after their <a href="http://news.cnet.com/8301-27080_3-10305200-245.html">networking attacks</a>, I got a torrent of <b>@cr_snake_bacon</b> tweets. Wasn&#8217;t sure why, but it seemed suspicious. Twitter&#8217;s API had flopped around for most of the day, so the logs were full of Exceptions and &#8230; <em>oops!</em> &#8230; re-connect attempts!</p>
<p>Of course I&#8217;d built the bots to re-tweet on an Exception. They&#8217;re all configured to wait 60 seconds, then try again. But of course until I fixed the configuration over night, they did <em>exactly what a bot would do</em> &#8230; <span style="color: #c00; font-style: italic;"><blink>conspicuous</blink></span> &#8230;</p>
<p>The service attacks on Twitter <a href="http://www.techcrunch.com/2009/08/07/geopolitical-attacks-on-twitter-intensified-almost-tenfold-last-night/">continued through today</a>, and I&#8217;m sure that the birdy techs are furiously building black ice fortresses in Scala even now. Again, I saw a burst this afternoon from <strong>all</strong> of my bots. <a href="http://twitter.com/cr_pokey">Pokey the Penguin</a>,<a href="http://twitter.com/cr_conet"> Conet Project</a>, and <a href="http://twitter.com/cr_wookie">Chewbacca</a> all had several things to say, all at once. Obviously I had fucked something else up, so I hurriedly checked the logs. And nope &#8230; actually, my change had worked &#8230; Twitter had just un-blocked my IP.</p>
<p><em>*whew*</em></p>
<p>I&#8217;m not exactly sure how many bots are out there &#8230; here&#8217;s a <a href="http://twitter.pbworks.com/Bots">nice wiki</a> being kept of them. But I can imagine I&#8217;m not the only one who made that try-again coding mis-calculation. What&#8217;s sweet is that it&#8217;s un-done now, and my toys can continue prattling on.</p>
<p>Thanks, guys. Sorry we looked like a vicious autonoma for a while there. Glad to be back.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/snake-n-bacon-in-the-ddos-caper/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Prowl = Growl iPhone push notifications</title>
		<link>http://blog.cantremember.com/prowl-growl-iphone-push-notifications/</link>
		<comments>http://blog.cantremember.com/prowl-growl-iphone-push-notifications/#comments</comments>
		<pubDate>Thu, 09 Jul 2009 06:47:18 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Productivity]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=159</guid>
		<description><![CDATA[i just installed Prowl, and it&#8217;s a handy little beast. it allows me to route my locally-issued Growl notifications to their Apple Push Notification server, which then routes them to the Prowl iPhone client. it all makes a simple, sweet, and extremely versatile combination the hierarchy of preferences is somewhat distributed however &#8230; the Prowl [...]]]></description>
			<content:encoded><![CDATA[<p>i just installed <a href="http://prowl.weks.net/"><strong>Prowl</strong></a>, and it&#8217;s a handy little beast.  it allows me to route my locally-issued <a href="http://growl.info/"><strong>Growl</strong></a> notifications to their <strong>Apple Push Notification</strong> server, which then routes them to the Prowl iPhone client.  it all makes a simple, sweet, and extremely versatile combination</p>
<p>the hierarchy of preferences is somewhat distributed however &#8230;</p>
<ul>
<li>
the <a href="http://prowl.weks.net/installation.php">Prowl site</a> will explain all the installation stuff.  the first level of preferences is configuring the Prowl plug-in to be your <strong>default Growl notification route</strong>.  it&#8217;s good that the Mac plug-in allows you to choose a pass-through notification method so that you will still see the events locally
</li>
<li>
then there&#8217;s the <strong>iPhone client preferences</strong>, under Settings.  they explain in the <a href="http://prowl.weks.net/faq.php">FAQ</a> that you need to change your settings, then <em>start up the Prowl client</em> to commit them to the server.  confusing, because your changes don&#8217;t take effect until that commit, so it&#8217;s just a matter of knowing.  also, the Settings only allow you to disable Sound <em>while the app is running</em> (which is a nice feature)
</li>
<li>
it took me a further read to figure out that i needed to disable the <i>background push</i> Sound &#038; Vibrate feature through the general <strong>Notification Settings</strong>.  you can tweak the Prowl app so that it&#8217;s seen and not heard
</li>
</ul>
<p>i&#8217;m a big fan of <a href="https://addons.mozilla.org/en-US/firefox/addon/5081">TwitterFox</a> because it&#8217;s equally simple &#038; straightforward.  i chose <a href="http://uri.cat/software/Scalaris/"><strong>Scalaris</strong></a> as my Mac Twitter client of choice for the same reason &#8212; i don&#8217;t needs one of the feature-rich hefty ones</p>
<p>eventually i&#8217;ll be updating my AWS health checks to use the <a href="http://prowl.rubyforge.org/"><strong>prowl</strong></a> Ruby gem.  very thoughtful!</p>
<p>thanks to <a href="http://twitter.com/laughingsquid">@laughingsquid</a> for the tip on Prowl.  i&#8217;m looking forward to a further geometrical increase in information overload!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/prowl-growl-iphone-push-notifications/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>random problem in your cloud-hosted app?  try a new instance!</title>
		<link>http://blog.cantremember.com/random-problem-in-your-cloud-hosted-app/</link>
		<comments>http://blog.cantremember.com/random-problem-in-your-cloud-hosted-app/#comments</comments>
		<pubDate>Wed, 27 May 2009 06:02:00 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[ops]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=152</guid>
		<description><![CDATA[chalk this one up under &#8216;Time Sunk&#8217;. my Ambience for the Masses app is a Spring / Hibernate / JSP stack, with a couple of other sweet components. i run it in on an AWS instance. it&#8217;s been purring along just wonderfully for months now. then, about ten days ago, it just stopped working normally [...]]]></description>
			<content:encoded><![CDATA[<p>chalk this one up under &#8216;Time Sunk&#8217;.</p>
<p>my <a href="http://sleepbot.com/seb">Ambience for the Masses</a> app is a Spring / Hibernate / JSP stack, with a couple of other sweet components.  i run it in on an <a href="http://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud">AWS</a> instance.  it&#8217;s been purring along just wonderfully for months now.  then, about ten days ago, it just <em>stopped working</em></p>
<p>normally Java apps don&#8217;t die without throwing some sort of Exception.  but that&#8217;s just what was happening.  so i stripped out various components &#8212; thank you, <a href="http://www.martinfowler.com/articles/injection.html">Dependency Injection</a> pattern! &#8212; and found that it would sometimes die instantly (if i was lucky) but usually it took a couple of hours.  i don&#8217;t have a lot of free time to track down random intermittent bullshit like this, so it took me about a week to boil it down</p>
<p>it was somewhere in the <a href="http://sleepbot.com/ambience/broadcast/map.html">Current Listener Map</a> &#8212; my Shoutcast-listener-tracking geo-positioning statistics-gathering data sculpture back-end engine.  i hear that the kids call them things <em>mash-ups</em>.  the geo-lookup APIs were the most delicate part, and it seemed to work with them omitted.  that red herring aside, it turned out to be the IP address resolution</p>
<div id="gist-2167130" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="k">try</span> <span class="o">{</span></div><div class='line' id='LC2'>	<span class="k">if</span> <span class="o">(</span><span class="n">getLog</span><span class="o">().</span><span class="na">isTraceEnabled</span><span class="o">())</span></div><div class='line' id='LC3'>		<span class="n">getLog</span><span class="o">().</span><span class="na">trace</span><span class="o">(</span><span class="s">&quot;lookup : &quot;</span> <span class="o">+</span> <span class="n">hostname</span><span class="o">);</span></div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'>	<span class="c1">// oh, lookup!</span></div><div class='line' id='LC6'>	<span class="n">InetAddress</span> <span class="n">ipAddress</span> <span class="o">=</span> <span class="n">InetAddress</span><span class="o">.</span><span class="na">getByName</span><span class="o">(</span><span class="n">hostname</span><span class="o">);</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>	<span class="c1">// NOTE: this is where it went bad on the AWS image</span></div><div class='line' id='LC9'>	<span class="c1">//   *sigh*</span></div><div class='line' id='LC10'><br/></div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">return</span> <span class="n">ipAddress</span><span class="o">.</span><span class="na">getHostAddress</span><span class="o">();</span></div><div class='line' id='LC12'><span class="o">}</span></div><div class='line' id='LC13'><span class="k">catch</span> <span class="o">(</span><span class="n">UnknownHostException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span> <span class="o">}</span></div><div class='line' id='LC14'><span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span></div><div class='line' id='LC15'>	<span class="n">getLog</span><span class="o">().</span><span class="na">warn</span><span class="o">(</span><span class="s">&quot;failed to lookup &quot;</span> <span class="o">+</span> <span class="n">hostname</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span></div><div class='line' id='LC16'><span class="o">}</span></div><div class='line' id='LC17'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167130/9d115582b22358b5a3e91dd890cd0a4739a75e06/example-gist.java" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167130#file_example_gist.java" style="float:right;margin-right:10px;color:#666">example-gist.java</a>
            <a href="https://gist.github.com/2167130">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>that block was just a couple of lines until i&#8217;d added all the logging and desperate Exception handling.  the app launches a lot of threads, so tracking down the issue was annoying &#8230; but eventually there it was in the traces.  the stack just terminated when the app tried to <code>getHostAddress</code> (not during <code>getByName</code> though &#8230; must be a lazy-loading thing)</p>
<p>so i nearly had it all tracked down to that.  then Tomcat <em>inexplicably</em> became unable to find basic JARs in <code>/usr/share/java</code> &#8212; i was using Fedora 8&#8242;s RPM version vs. raw Apache, and it&#8217;s organized real funny-like</p>
<p>so i threw up my hands and started up a fresh instance of my webapp AWS image.  i&#8217;d rebooted the existing one, and that hadn&#8217;t helped at all.  of course, the issue <strong>magically disappeared</strong> on the new instance.  did anyone see that coming from a distance?  ya probably did.  cuz it&#8217;s ironic.  and it&#8217;s the title line of the damn blog post</p>
<p>the hostname resoultion is surely a low-level OS thing.  both Linux JavaSE 6 and IcedTea 7 just shat the bed when they got to that point, unlikely unless they both leveraged the same lib call.  something must have gone wonk in the virtualization, and apparently a key part of the solution was running it inside of a different farm.  i wasted a helluva lot of time to find that out</p>
<p><strong>lesson !!</strong>  if weird inexplicable freaky-ass things start happening to your cloud-hosted app, load it up on a new VM <em>earlier than later</em>.  i&#8217;d taken a late-stage backup of the failed instance and assumed it would be corrupted with the Mystery Bug (read as: a waste of time to attempt). but oh no, it worked just great :) .  next time it&#8217;ll be a cinch to just bundle the instance up, image it, and use it to launch a new one</p>
<p>and ultimately &#8230; <em>it wasn&#8217;t a bug in my code !!!</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/random-problem-in-your-cloud-hosted-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>being too rapid on the things that matter</title>
		<link>http://blog.cantremember.com/being-too-rapid-on-the-things-that-matter/</link>
		<comments>http://blog.cantremember.com/being-too-rapid-on-the-things-that-matter/#comments</comments>
		<pubDate>Fri, 08 May 2009 05:04:51 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[mentor]]></category>
		<category><![CDATA[startup]]></category>
		<category><![CDATA[tdd]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=144</guid>
		<description><![CDATA[it took me a while to come up with the title for this post. and it&#8217;s and Opinion Piece, not Techincal &#8230; so you&#8217;ll see why &#8230; i&#8217;m working for a new company now, and they&#8217;re rocking it for RoR apps on the iPhone. sounds like a good place to be. one of the many [...]]]></description>
			<content:encoded><![CDATA[<p>it took me a while to come up with the title for this post.  and it&#8217;s and Opinion Piece, not Techincal &#8230; so you&#8217;ll see why &#8230;</p>
<p>i&#8217;m working for a new company now, and they&#8217;re rocking it for RoR apps on the iPhone. sounds like a good place to be. one of the many reasons why this position works for me is because these guys are all about GTD and getting it out there. lean &#8216;n&#8217; mean</p>
<p>whereas i&#8217;ve become very used to a holistic detail-orented, wisened test-backed process. great for Enterprise, but not so much for the reckless streets of Startup 3.0 .  so i&#8217;m in a learning process. i&#8217;ve turned around some good stuff quickly, and it&#8217;s very satisfying</p>
<p>but i&#8217;ve screwed the pooch twice since i&#8217;ve been there.  it&#8217;s totally a judgement call thing &#8212; i&#8217;m shooting <i>too</i> fast from the hip, and don&#8217;t feel like i really grasp the balance here &#8230;</p>
<p>first project i worked on was related to account management. they wanted a quick turn-around, i gave it a shot, had the whole thing backed with solid testing, and ready for on-time deployment with a smile. and in trying to keep track of all the new system permutations &#8212; i&#8217;d been there 2 weeks or so &#8212; i forgot one basic thing, and forgot to test for another. a nice little Perfect Storm. one emergency 1am database rollback later, we had a load of pissed customers and a helluva lot of explaining to do</p>
<p>so, then this past week, i went in to fix a minor rounding issue bug.  those can be touchy.  the <b>right</b> way to do it is with <a href="http://www.ruby-doc.org/stdlib/libdoc/bigdecimal/rdoc/classes/BigDecimal.html"><code>BigDecimal</code></a>. yep, i&#8217;ve done that in Java too with <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html"><code>BigDecimal</code></a>.  overall, it&#8217;s somewhat ponderous, detail-oriented, and can easily be polluted with <code>Float</code>s and the like.  so i&#8217;d taken a shortcut, realizing that the low-level C impl was doing String conversion without the rounding issue.  so i took the low-hanging fruit:</p>
<div class="pre_wrap">
<pre><code>total.to_s.to_i
</code></pre>
</div>
<p><i>awesome !!1!</i>. well, that is until you get into the 100-of-trillions area, otherwise shown as <code>1.0e+14</code>. guess what happens when you parse that into a Fixnum? no database rollback this time, but Da Boss had to spend <i>days</i> sorting out the visceral impact of ridiculous sums of bogus exploit money pouring into our RPG</p>
<p>security, privacy and account management.  payment calculations.  not the sort of things to take shortcuts on.  yet, if you&#8217;re embracing a culture that wants it done quickly and with minimum impact, it&#8217;s a risk you might be willing to take.  it&#8217;s not like i didn&#8217;t have test scripts &#8230; i just forgot to head into scientific notation territory.  just like i forgot to check for the implication of null password acceptance <em>( long story there, special account cases, etc. )</em></p>
<p>i&#8217;m putting these things up here for my fellow developers to laugh at. &nbsp; &#8220;I mean, c&#8217;mon. All that&#8217;s totally obvious stuff.&#8221; &nbsp; &#8220;I&#8217;d never miss that, that&#8217;s sophmore shit.&#8221; &nbsp; good, get it out of your system, laughing boy</p>
<p>but believe me, when you&#8217;re on the other end of it, and had been in the middle of it and all full of all the other things that you needed to keep track of at that time, heh, well, that&#8217;s when you&#8217;ll really need to keep yerself laughing :)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/being-too-rapid-on-the-things-that-matter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dynamic Tunneling for your Facebook App</title>
		<link>http://blog.cantremember.com/dynamic-tunneling-for-your-facebook-app/</link>
		<comments>http://blog.cantremember.com/dynamic-tunneling-for-your-facebook-app/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 02:14:08 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[tunnel]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=132</guid>
		<description><![CDATA[When I was launching Forgiveness, my first attempt at a Facebook app via RoR, I ran into one of the typical developer quandries. How the heck to I actually make local revs to the app and proxy them through the FB back-end while still keeping the app running live for my millions of satisfied customers? [...]]]></description>
			<content:encoded><![CDATA[<p>When I was launching <a href="http://apps.facebook.com/forgiveness-app/">Forgiveness</a>, my first attempt at a Facebook app via RoR, I ran into one of the typical developer quandries.  How the heck to I actually make local revs to the app and proxy them through the FB back-end while still keeping the app running live for my millions of satisfied customers?</p>
<p>I sought advice from <a href="http://www.workingwithrails.com/person/11513-steve-enzer">Steve Enzer</a>, and his answer was <em>&#8216;create a separate FB app id&#8217;</em>.  Yep, you can do that.  I&#8217;m sure it works just fine.  However, I wanted to come up with a single-app solution, you know, just because.</p>
<p>My app backbone was based upon <a href="http://rubyforge.org/projects/facebooker"><strong>Facebooker</strong></a>, a great gem for just such purposes.  And assuming that you&#8217;re using that gem, you&#8217;re provided with:</p>
<div class="pre_wrap">
<pre><code>rake facebooker:tunnel:start
</code></pre>
</div>
<p>Which starts up the <a href="http://blog.evanweaver.com/articles/2007/07/13/developing-a-facebook-app-locally/">traditional server tunnel</a> to your local dev box; Facebook&#8217;s proxy won&#8217;t know the difference.  I&#8217;ve also set up my canvas page URLs and the like with a fixed IP &#8212; according to FB&#8217;s best practices, because they don&#8217;t want to deal with all the DNS resolution stuff &#8212; and it listens on port 80 with a dedicated context.  So, I&#8217;ve got my <code>facebooker.yml</code> set up for port 3100:</p>
<div class="pre_wrap">
<pre><code>tunnel:
  public_host: cantremember.com
  public_port: 3100
  local_port: 3100
</code></pre>
</div>
<p>And here&#8217;s my nginx config:</p>
<div id="gist-2167164" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>#</div><div class='line' id='LC2'>#	included into vhosts/localhost.conf (*.by-ip)</div><div class='line' id='LC3'>#		answers to :80</div><div class='line' id='LC4'>#</div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'>#	registered with FaceBook</div><div class='line' id='LC7'>location /forgiveness {</div><div class='line' id='LC8'>	#	no-trailing-/ case (the rewritten URI has a zero length)</div><div class='line' id='LC9'>	if ($request_filename = &#39;&#39;) {</div><div class='line' id='LC10'>		rewrite  (.*)  $1/;</div><div class='line' id='LC11'>	}</div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'>	#	the only way i could figure to get conditional proxying</div><div class='line' id='LC14'>	#		proxy_pass via variables = no</div><div class='line' id='LC15'>	#		if { proxy_pass } = no</div><div class='line' id='LC16'>	#		so ... two more goofy contexts</div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'>	#	magic tunnel cookie (any value)</div><div class='line' id='LC19'>	if ($http_cookie ~ &#39;_forgiveness_tunnel=1&#39;) {</div><div class='line' id='LC20'>		#	last : stop rewriting, but re-process through nginx (vs. break)</div><div class='line' id='LC21'>		rewrite  /forgiveness(.*)  /forgiveness-dev$1  last;</div><div class='line' id='LC22'>	}</div><div class='line' id='LC23'>	rewrite  /forgiveness(.*)  /forgiveness-pub$1;</div><div class='line' id='LC24'>}</div><div class='line' id='LC25'><br/></div><div class='line' id='LC26'>location /forgiveness-pub {</div><div class='line' id='LC27'>	#	root contextify</div><div class='line' id='LC28'>	#	break to terminate re-processing through nginx</div><div class='line' id='LC29'>	rewrite /forgiveness-pub(.*)  $1  break;</div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'>	proxy_pass  http://cantremember_forgiveness_cluster;</div><div class='line' id='LC32'>}</div><div class='line' id='LC33'><br/></div><div class='line' id='LC34'>location /forgiveness-dev {</div><div class='line' id='LC35'>	rewrite /forgiveness-dev(.*)  $1  break;</div><div class='line' id='LC36'><br/></div><div class='line' id='LC37'>	#	same as public upstream</div><div class='line' id='LC38'>	proxy_pass  http://127.0.0.1:3100;</div><div class='line' id='LC39'>}</div><div class='line' id='LC40'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167164/912b2d08c143d06521220eae742d810c0d2dee1d/facebook-tunnel-nginx.conf" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167164#file_facebook_tunnel_nginx.conf" style="float:right;margin-right:10px;color:#666">facebook-tunnel-nginx.conf</a>
            <a href="https://gist.github.com/2167164">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Here I omit the upstream cluster configs, and I&#8217;ll let the comments speak for themselves &#8230; I always try to keep track of red herrings at least at some level, and there were several I bumped into while trying out this solution.  The best approach that I found was to simply set a cookie with a naming convention that nginx could parse (yes, there is a regex performance penalty here).  The development context is routed locally, which is where the tunnel is waiting to pick up and run with it.</p>
<p>Setting the cookie, well that&#8217;s another trick.  Though not much of one.  Of course your app is completely masked behind the <code>apps.facebook.com</code> domain, so trying to set a cookie to your internal domain (eg. IP) through your browser isn&#8217;t so easy, and it&#8217;s repetetive manual work.  So, just bake it into a dedicated action in your app:</p>
<div id="gist-2167194" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="c1">#	allows for Facebook pub / dev tunneling</span></div><div class='line' id='LC2'><span class="c1">#		have to set the Cookie within the scope of the app</span></div><div class='line' id='LC3'><span class="c1">#		can&#39;t use Firefox cookie editor to hack it</span></div><div class='line' id='LC4'><span class="k">def</span> <span class="nf">tunnel</span></div><div class='line' id='LC5'>	<span class="c1">#	how convenient!</span></div><div class='line' id='LC6'>	<span class="n">value</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span></div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>	<span class="c1">#	this is how we get pub / dev tunneling</span></div><div class='line' id='LC9'>	<span class="c1">#		see support/nginx</span></div><div class='line' id='LC10'>	<span class="k">if</span> <span class="n">value</span> <span class="o">==</span> <span class="s1">&#39;nil&#39;</span></div><div class='line' id='LC11'>		<span class="n">cookies</span><span class="o">.</span><span class="n">delete</span> <span class="ss">:_forgiveness_tunnel</span></div><div class='line' id='LC12'>	<span class="k">else</span></div><div class='line' id='LC13'>		<span class="n">cookies</span><span class="o">[</span><span class="ss">:_forgiveness_tunnel</span><span class="o">]</span> <span class="o">=</span> <span class="n">value</span></div><div class='line' id='LC14'>	<span class="k">end</span></div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'>	<span class="c1">###render :text =&gt; &quot;OK #{value}&quot;, :content_type =&gt; &#39;text/plain&#39;</span></div><div class='line' id='LC17'>	<span class="n">redirect_to</span> <span class="n">root_url</span></div><div class='line' id='LC18'><span class="k">end</span></div><div class='line' id='LC19'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167194/a9b8ef5efc99c10235326627398cb72f75b89d3a/facebook_tunnel_action.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167194#file_facebook_tunnel_action.rb" style="float:right;margin-right:10px;color:#666">facebook_tunnel_action.rb</a>
            <a href="https://gist.github.com/2167194">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Then use <code>/:controller/tunnel/1</code> to engage, and <code>/:controller/tunnel/0</code> to disengage.  A little bit of auth logic, and you&#8217;re good to go.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/dynamic-tunneling-for-your-facebook-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>finding your iPhone&#8217;s UDID after you&#8217;ve made it inoperable</title>
		<link>http://blog.cantremember.com/finding-your-iphones-udid-after-youve-made-it-inoperable/</link>
		<comments>http://blog.cantremember.com/finding-your-iphones-udid-after-youve-made-it-inoperable/#comments</comments>
		<pubDate>Fri, 20 Mar 2009 09:50:27 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=125</guid>
		<description><![CDATA[okay, this is what happens when you rush forward into things &#8230; i ponied up cash for the Apple Developer License a while back. and lo &#038; behold, developers can download the DMG for the 3.0 Beta firmware update from the iPhone Dev Center! so i went and installed iPhone OS 3.0 Beta tonight &#8230; [...]]]></description>
			<content:encoded><![CDATA[<p>okay, this is what happens when you rush forward into things &#8230;</p>
<p>i ponied up cash for the Apple Developer License a while back. and lo &#038; behold, developers can download the DMG for the 3.0 Beta firmware update from the <a href="http://developer.apple.com/iphone/index.action">iPhone Dev Center</a>! so i went and installed iPhone OS 3.0 Beta tonight &#8230; i wanted to check out the cut&#8217;n'paste capabilities, etc.  downloaded and extracted the firmware update package, started iTunes, held down [Ctrl+Option] when clicking &#8216;Check for Update&#8217; to bring up the file selector dialog, and installed the 3.0 Beta IPSW</p>
<p>great! hooray! it&#8217;s the usual slow process. however, once the device restarts, it goes into pre-activation mode &#8230; and iTunes rejects it as not behing a registered development device.  open up the <a href="http://developer.apple.com/iphone/manage/devices/index.action">Device Management Portal</a>, and it tells you how about Locating a Unique Device ID &#8230; <em>&#8220;The 40 hex character string in the Identifier field is your device’s UDID.&#8221;</em> i believe they refer to it as an ICCID in other places</p>
<p>alright, where can one find this magical string? well, it&#8217;s shown when your device is connected to iTunes &#8230; but of course, that&#8217;s the problem! it already won&#8217;t accept my device = meta-problem. so, then i read further &#8230; <em>&#8220;Please DO NOT install the iPhone OS before registering device UDIDs, as installation on non-registered devices will render them inoperable.&#8221;</em></p>
<p>so i figured that i&#8217;m toast. i <a href="http://support.apple.com/kb/HT1267">can easily find</a> the Serial Number and IMEI, but not the UDID, at least according to Apple&#8217;s instructions</p>
<p>making a long story short &#8230; look at System Profiler (if you haven&#8217;t already) &#8230; under <strong>USB :: USB High Speed Bus :: iPhone :: Serial Number</strong>. register that as a developer device, and you&#8217;re good to go as soon as you re-boot the phone &amp; iTunes. there&#8217;s a much more elaborate description of the process using Xcode, but it doesn&#8217;t seem to be mandatory cause i sure ain&#8217;t done that</p>
<p>and yes &#8230; cut&#8217;n'paste is really nice!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/finding-your-iphones-udid-after-youve-made-it-inoperable/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>When Broken Toys Impact your Friends</title>
		<link>http://blog.cantremember.com/when-broken-toys-impact-your-friends/</link>
		<comments>http://blog.cantremember.com/when-broken-toys-impact-your-friends/#comments</comments>
		<pubDate>Fri, 06 Feb 2009 22:21:07 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[bot]]></category>
		<category><![CDATA[mentor]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=114</guid>
		<description><![CDATA[This sure was an interesting morning! I woke up to find that I&#8217;d unintentionally sent direct messages to all of the followers on my personal Twitter account. And I&#8217;d sent them out at 1a PST, which means that anyone who (a) uses SMS capabilities, and (b) has some text message notification sound set up would [...]]]></description>
			<content:encoded><![CDATA[<p>This sure was an interesting morning!  I woke up to find that I&#8217;d unintentionally sent direct messages to <strong>all</strong> of the followers on my <a href="http://twitter.com/sleepbotzz">personal Twitter account</a>.  And I&#8217;d sent them out at 1a PST, which means that anyone who <em>(a)</em> uses SMS capabilities, and<em> (b)</em> has some text message notification sound set up would have been rudely interrupted in the middle of the night.</p>
<p>Fortunately, I haven&#8217;t lost any followers (yet).  But this was a perfect case of how mixing business with pleasure can have unintended consequences.</p>
<h3>What Have I Learned</h3>
<p>Or rather, what have I <em>re-learned</em> &#8230;</p>
<dl>
<dt><strong>Soft-disable features in Production at Launch Time</strong></dt>
<dd>&nbsp;<br />
My Twitter engines are built with both an <code>:enable_tweet</code> and <code>:enable_greeting</code> config setting.  In the git repo, they&#8217;re both <code>true</code>.  When I did my local testing, I&#8217;d disabled them correctly.  When I launched in Production, I neglected to make the quick-and-dirty changes; after all, everything worked great.  And once started, my scripts correctly responded to the no-initial-state condition, and greeted <em>everybody</em>.</p>
<p>Launch preparation is critical, even for little projects.  The start-up mentality is to move fast and lean, but there&#8217;s such as thing as too fast, and probably as too lean too.  Gradual uptake migration is a wise strategy even for the &#8216;little things&#8217;.
</dd>
<dt><strong>Mock and Integration Testing Only Gets You So Far</strong></dt>
<dd>&nbsp;<br />
I used <a href="http://rspec.info">rspec</a> to mock out the full capabilities of the engine.  Found some real-world issues, resolved them.  I also wrote some core <a href="http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">integration tests</a>, ran them locally.  Immediate failures. I had mocked documented features that <em>didn&#8217;t actually exist</em>.  Fixed, re-mocked, re-tested, fixed again, <em>etc</em> .</p>
<p>Another great reminder that you can only mock something you trust, and how can you trust something you haven&#8217;t actually run under integration conditions to start with!  Re-tested integration, and everything passed with flying colors.  Sure, the features worked great now!  And when I launched them, they did exactly what I asked.</p>
<p>So, as if we haven&#8217;t heard it enough times, be careful what you ask for!
</dd>
</dl>
<p>All of this is familiar to anyone who has made a mistake in the software industry.  It&#8217;s not like I haven&#8217;t successfully executed dozens of critical launches in the past, and most with virtually no issues at all.  But what&#8217;s interesting is what happens when these mistakes happen in a public forum, and whom you expose them to &#8212; say, <em>your friends</em> :)</p>
<p>And who can say when two ounces of caution is more deserving than one &#8230; without the benefit of hindsight.</p>
<p>Just ask anyone who has a stringent backup policy how much time &#038; effort they invest to avoid an event that may never actually happen.  That stringency usually comes from that one unforgettable experience, and from there is born an extra layer of caution, and an additional time-sink (eg. mock &#038; integration testing)</p>
<p>Heh.  <a href="http://en.wikipedia.org/wiki/Keep_it_simple_stupid">KISS</a>.  So, what exactly is simple?  <a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a>.  Isn&#8217;t that supposed to be a time-saver?  Well, it depends on what you&#8217;re not trying to repeat.  Strange how these cuddly and liberating acronyms can have more than one interpretation.</p>
<p>Experience taints everything.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/when-broken-toys-impact-your-friends/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Twitter4R shifts RSpec onto my Front Burner</title>
		<link>http://blog.cantremember.com/twitter4r-shifts-rspec-onto-my-front-burner/</link>
		<comments>http://blog.cantremember.com/twitter4r-shifts-rspec-onto-my-front-burner/#comments</comments>
		<pubDate>Thu, 05 Feb 2009 09:00:33 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[bdd]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[tdd]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=94</guid>
		<description><![CDATA[As usual, my day did not pan out as expected. But, also as usual, I learned a lot! Coming Up To Speed on Rspec So, learning rspec has been on my list for a while. I finally got around to it. Nice framework. I am familiar with EasyMock, and am aware of JMock. I never [...]]]></description>
			<content:encoded><![CDATA[<p>As usual, my day did not pan out as expected.  But, also as usual, I learned a lot!</p>
<h3>Coming Up To Speed on Rspec</h3>
<p>So, learning <a href="http://rspec.info/"><strong>rspec</strong></a> has been on my list for a while.  I finally got around to it.  Nice framework.  I am familiar with <a href="http://www.easymock.org/">EasyMock</a>, and am aware of <a href="http://www.jmock.org/">JMock</a>.  I never had an opportunity to get into <a href="http://code.google.com/p/mockito/">Mockito</a>, but I&#8217;d give it a glance next time I put on my Java hat.</p>
<p>There is some decent documentation on it out there.  I found these links particularly helpful:</p>
<ul>
<li>The <a href="http://rspec.info/documentation/">formal documentation</a>.</li>
<li>David Chelimsky&#8217;s <a href="http://blog.davidchelimsky.net/articles/2007/05/14/an-introduction-to-rspec-part-i">from-the-ground-up description</a></li>
<li>rspec.info&#8217;s drill-down into <a href="http://rspec.info/documentation/mocks/">Mocks and Stubs</a></li>
<li>Luke Redpath&#8217;s cross-over into <a href="http://www.lukeredpath.co.uk/blog/developing-a-rails-model-using-bdd-and-rspec-part-1.html">usage under Rails</a></li>
</ul>
<p>During my ramping-up, I took the <a href="http://blog.cantremember.com/learning-ruby-through-assertions-and-podcasts/">usual meta-approach</a> of creating a test suite &#8212; and <em>yaaay</em>, that&#8217;s what <code>rspec</code> is meant for! &#8212; which I then used to test out its own range of capabilities.  The <a href="http://rspec.rubyforge.org/rspec/1.1.12/">Modules in the rdoc</a> which I&#8217;ve found to provide the most value are:</p>
<ul>
<li><strong>Spec::Expectations::ObjectExpectations</strong> for conditionals (eg. <code>should</code> &#038; <code>should_not</code>)</li>
<li><strong>Spec::Matchers</strong> for expectations (eg. <code>equal(<em>value</em>)</code>, <code>be_a(<em>class</em>)</code>, <code>respond_to(<em>method_sym</em>)</code>, <code>raise_error</code>)</li>
<li><strong>Spec::Mocks::Methods</strong> for mock method definition (eg. <code>should_receive(<em>method_sym</em>)</code>)</li>
<li><strong>Spec::Mocks::MessageExpectation</strong> for mock behaviour (eg. <code>with(<em>*args</em>)</code>, <code>once</code>, <code>exactly(<em>n</em>).times</code>, <code>any_number_of_times</code>)</li>
<li><strong>Spec::Mocks::ArgumentConstraints</strong> for mock arguments (eg. <code>an_instance_of(<em>class</em>)</code>, <code>anything</code>)</li>
<li><strong>Spec::Mocks::BaseExpectation</strong> for mock responses (eg. <code>and_return(<em>value</em>)</code>, <code>and_yield(<em>&#038;block</em>)</code>)</li>
</ul>
<p>I won&#8217;t got into the deep details, but here are some examples of conditionals and expectations that I paraphrased into my meta-test:</p>
<div id="gist-2167214" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="n">specify</span> <span class="s2">&quot;knowledge of nil&quot;</span> <span class="k">do</span></div><div class='line' id='LC2'>	<span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">should_not</span> <span class="n">be_nil</span></div><div class='line' id='LC3'><span class="k">end</span></div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'><span class="n">specify</span> <span class="s2">&quot;numeric calculations&quot;</span> <span class="k">do</span></div><div class='line' id='LC6'>	<span class="p">(</span><span class="mi">355</span><span class="o">.</span><span class="mi">0</span> <span class="o">/</span> <span class="mi">113</span><span class="p">)</span><span class="o">.</span><span class="n">should</span> <span class="n">be_close</span><span class="p">(</span><span class="no">Math</span><span class="o">::</span><span class="no">PI</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">1</span><span class="p">)</span></div><div class='line' id='LC7'><span class="k">end</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'><span class="n">specify</span> <span class="s2">&quot;changes made by a closure&quot;</span> <span class="k">do</span></div><div class='line' id='LC10'>	<span class="n">array</span> <span class="o">=</span> <span class="o">[]</span></div><div class='line' id='LC11'>	<span class="nb">lambda</span> <span class="p">{</span></div><div class='line' id='LC12'>		<span class="n">array</span> <span class="o">&lt;&lt;</span> <span class="ss">:a</span></div><div class='line' id='LC13'>	<span class="p">}</span><span class="o">.</span><span class="n">should</span> <span class="n">change</span><span class="p">(</span><span class="n">array</span><span class="p">,</span> <span class="ss">:size</span><span class="p">)</span></div><div class='line' id='LC14'><span class="k">end</span></div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'><span class="n">specify</span> <span class="s2">&quot;equality&quot;</span> <span class="k">do</span></div><div class='line' id='LC17'>	<span class="mi">5</span><span class="o">.</span><span class="n">should</span> <span class="n">eql</span> <span class="mi">5</span></div><div class='line' id='LC18'>	<span class="mi">5</span><span class="o">.</span><span class="n">should</span> <span class="n">equal</span> <span class="mi">5</span></div><div class='line' id='LC19'>	<span class="o">[</span><span class="ss">:a</span><span class="o">].</span><span class="n">should_not</span> <span class="n">equal</span> <span class="o">[</span><span class="ss">:a</span><span class="o">]</span></div><div class='line' id='LC20'><span class="k">end</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'><span class="n">specify</span> <span class="s2">&quot;raising of errors&quot;</span> <span class="k">do</span></div><div class='line' id='LC23'>	<span class="c1">#	always construct fresh!!!</span></div><div class='line' id='LC24'>	<span class="c1">#		otherwise it won&#39;t be re-wrappable</span></div><div class='line' id='LC25'>	<span class="k">def</span> <span class="nf">raiser</span><span class="p">(</span><span class="n">s</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span></div><div class='line' id='LC26'>		<span class="nb">lambda</span> <span class="p">{</span> <span class="k">raise</span><span class="p">(</span><span class="no">RuntimeError</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span> <span class="k">if</span> <span class="n">s</span> <span class="p">}</span></div><div class='line' id='LC27'>	<span class="k">end</span></div><div class='line' id='LC28'><br/></div><div class='line' id='LC29'>	<span class="n">raiser</span><span class="p">(</span><span class="s1">&#39;x&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">should</span> <span class="n">raise_error</span></div><div class='line' id='LC30'>	<span class="n">raiser</span><span class="p">(</span><span class="s1">&#39;y&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">should</span> <span class="n">raise_error</span><span class="p">(</span><span class="no">RuntimeError</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">)</span></div><div class='line' id='LC31'><span class="k">end</span></div><div class='line' id='LC32'><br/></div><div class='line' id='LC33'><span class="n">specify</span> <span class="s2">&quot;closure satisfaction&quot;</span> <span class="k">do</span></div><div class='line' id='LC34'>	<span class="mi">5</span><span class="o">.</span><span class="n">should</span> <span class="n">satisfy</span> <span class="p">{</span><span class="o">|</span><span class="n">n</span><span class="o">|</span> <span class="p">(</span><span class="mi">4</span><span class="o">.</span><span class="n">.</span><span class="mi">6</span><span class="p">)</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">}</span></div><div class='line' id='LC35'><span class="k">end</span></div><div class='line' id='LC36'><br/></div><div class='line' id='LC37'><span class="n">specify</span> <span class="s2">&quot;what lists do&quot;</span> <span class="k">do</span></div><div class='line' id='LC38'>	<span class="o">[].</span><span class="n">should</span> <span class="n">respond_to</span><span class="p">(</span><span class="ss">:each</span><span class="p">,</span> <span class="ss">:find</span><span class="p">,</span> <span class="ss">:size</span><span class="p">)</span></div><div class='line' id='LC39'><span class="k">end</span></div><div class='line' id='LC40'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167214/1ae41475448496bd2470611de40c2556255e5ecb/coming_up_to_spec.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167214#file_coming_up_to_spec.rb" style="float:right;margin-right:10px;color:#666">coming_up_to_spec.rb</a>
            <a href="https://gist.github.com/2167214">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>And here&#8217;s a little bit of silly mocking:</p>
<div id="gist-2167235" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="k">def</span> <span class="nf">expect_raise</span><span class="p">(</span><span class="n">type</span><span class="o">=</span><span class="no">Spec</span><span class="o">::</span><span class="no">Mocks</span><span class="o">::</span><span class="no">MockExpectationError</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span></div><div class='line' id='LC2'>	<span class="nb">abort</span> <span class="s1">&#39;block must be provided&#39;</span> <span class="k">unless</span> <span class="nb">block_given?</span></div><div class='line' id='LC3'>	<span class="n">block</span><span class="o">.</span><span class="n">should</span> <span class="n">raise_error</span><span class="p">(</span><span class="n">type</span><span class="p">)</span></div><div class='line' id='LC4'><span class="k">end</span></div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'><span class="n">specify</span> <span class="s2">&quot;the basics&quot;</span> <span class="k">do</span></div><div class='line' id='LC7'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span> <span class="ss">:hello</span></div><div class='line' id='LC8'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_not_receive</span> <span class="ss">:goodbye</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">hello</span></div><div class='line' id='LC11'>	<span class="n">expect_raise</span> <span class="p">{</span> <span class="vi">@mock</span><span class="o">.</span><span class="n">stay</span> <span class="p">}</span></div><div class='line' id='LC12'>	<span class="n">expect_raise</span> <span class="p">{</span> <span class="vi">@mock</span><span class="o">.</span><span class="n">goodbye</span> <span class="p">}</span></div><div class='line' id='LC13'><span class="k">end</span></div><div class='line' id='LC14'><br/></div><div class='line' id='LC15'><span class="n">specify</span> <span class="s2">&quot;how many times, and with what&quot;</span> <span class="k">do</span></div><div class='line' id='LC16'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:one</span><span class="p">)</span><span class="o">.</span><span class="n">once</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></div><div class='line' id='LC17'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">one</span> <span class="mi">1</span></div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:string</span><span class="p">)</span><span class="o">.</span><span class="n">exactly</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">times</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="n">an_instance_of</span><span class="p">(</span><span class="nb">String</span><span class="p">))</span></div><div class='line' id='LC20'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">string</span> <span class="s1">&#39;a string&#39;</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:anything</span><span class="p">)</span><span class="o">.</span><span class="n">exactly</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">times</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="n">any_args</span><span class="p">())</span></div><div class='line' id='LC23'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">anything</span></div><div class='line' id='LC24'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">anything</span> <span class="ss">:again</span></div><div class='line' id='LC25'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">anything</span> <span class="ss">:third</span><span class="p">,</span> <span class="s1">&#39;time&#39;</span></div><div class='line' id='LC26'><br/></div><div class='line' id='LC27'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:array_ish</span><span class="p">)</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="n">duck_type</span><span class="p">(</span><span class="ss">:each</span><span class="p">,</span> <span class="ss">:find</span><span class="p">,</span> <span class="ss">:size</span><span class="p">))</span></div><div class='line' id='LC28'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">array_ish</span> <span class="o">[</span><span class="ss">:item</span><span class="o">]</span></div><div class='line' id='LC29'><span class="k">end</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'><span class="n">specify</span> <span class="s2">&quot;what i return, raise or throw&quot;</span> <span class="k">do</span></div><div class='line' id='LC32'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:get_one</span><span class="p">)</span><span class="o">.</span><span class="n">and_return</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></div><div class='line' id='LC33'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:put_one</span><span class="p">)</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></div><div class='line' id='LC34'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">put_one</span> <span class="vi">@mock</span><span class="o">.</span><span class="n">get_one</span></div><div class='line' id='LC35'><br/></div><div class='line' id='LC36'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:increment</span><span class="p">)</span><span class="o">.</span><span class="n">any_number_of_times</span><span class="o">.</span><span class="n">with</span><span class="p">(</span><span class="n">instance_of</span><span class="p">(</span><span class="no">Fixnum</span><span class="p">))</span><span class="o">.</span><span class="n">and_return</span> <span class="p">{</span><span class="o">|</span><span class="n">i</span><span class="o">|</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}</span></div><div class='line' id='LC37'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">increment</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">should</span> <span class="n">equal</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span></div><div class='line' id='LC38'><br/></div><div class='line' id='LC39'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:raises_string</span><span class="p">)</span><span class="o">.</span><span class="n">and_raise</span><span class="p">(</span><span class="s1">&#39;something runtime&#39;</span><span class="p">)</span></div><div class='line' id='LC40'>	<span class="n">expect_raise</span><span class="p">(</span><span class="no">RuntimeError</span><span class="p">)</span> <span class="p">{</span> <span class="vi">@mock</span><span class="o">.</span><span class="n">raises_string</span> <span class="p">}</span></div><div class='line' id='LC41'><br/></div><div class='line' id='LC42'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:pitch</span><span class="p">)</span><span class="o">.</span><span class="n">and_throw</span><span class="p">(</span><span class="ss">:ball</span><span class="p">)</span></div><div class='line' id='LC43'>	<span class="nb">lambda</span> <span class="p">{</span> <span class="vi">@mock</span><span class="o">.</span><span class="n">pitch</span> <span class="p">}</span><span class="o">.</span><span class="n">should</span> <span class="n">throw_symbol</span><span class="p">(</span><span class="ss">:ball</span><span class="p">)</span></div><div class='line' id='LC44'><span class="k">end</span></div><div class='line' id='LC45'><br/></div><div class='line' id='LC46'><span class="n">specify</span> <span class="s2">&quot;yielding in a complex fashion&quot;</span> <span class="k">do</span></div><div class='line' id='LC47'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:gimmee</span><span class="p">)</span><span class="o">.</span><span class="n">exactly</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">times</span><span class="o">.</span><span class="n">and_yield</span><span class="p">(</span><span class="ss">:x</span><span class="p">)</span></div><div class='line' id='LC48'><br/></div><div class='line' id='LC49'>	<span class="n">holder</span> <span class="o">=</span> <span class="o">[]</span></div><div class='line' id='LC50'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">gimmee</span> <span class="p">{</span><span class="o">|</span><span class="n">value</span><span class="o">|</span> <span class="n">holder</span> <span class="o">&lt;&lt;</span> <span class="n">value</span> <span class="p">}</span></div><div class='line' id='LC51'>	<span class="n">holder</span><span class="o">.</span><span class="n">should</span> <span class="n">eql</span><span class="p">(</span><span class="o">[</span><span class="ss">:x</span><span class="o">]</span><span class="p">)</span></div><div class='line' id='LC52'>	<span class="mi">2</span><span class="o">.</span><span class="n">times</span> <span class="p">{</span> <span class="vi">@mock</span><span class="o">.</span><span class="n">gimmee</span> <span class="p">{</span><span class="o">|</span><span class="n">value</span><span class="o">|</span> <span class="n">holder</span> <span class="o">&lt;&lt;</span> <span class="n">value</span> <span class="p">}</span> <span class="p">}</span></div><div class='line' id='LC53'>	<span class="n">holder</span><span class="o">.</span><span class="n">should</span> <span class="n">eql</span><span class="p">(</span><span class="o">[</span><span class="ss">:x</span><span class="p">,</span> <span class="ss">:x</span><span class="p">,</span> <span class="ss">:x</span><span class="o">]</span><span class="p">)</span></div><div class='line' id='LC54'><span class="k">end</span></div><div class='line' id='LC55'><br/></div><div class='line' id='LC56'><span class="n">specify</span> <span class="s2">&quot;validation via closure&quot;</span> <span class="k">do</span></div><div class='line' id='LC57'>	<span class="p">(</span><span class="vi">@mock</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:threely</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="o">|</span></div><div class='line' id='LC58'>		<span class="n">value</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">size</span><span class="o">.</span><span class="n">should</span> <span class="n">eql</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span></div><div class='line' id='LC59'>	<span class="k">end</span><span class="p">)</span><span class="o">.</span><span class="n">exactly</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">times</span></div><div class='line' id='LC60'><br/></div><div class='line' id='LC61'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">threely</span> <span class="s1">&#39;x&#39;</span> <span class="o">*</span> <span class="mi">3</span></div><div class='line' id='LC62'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">threely</span> <span class="ss">:key</span></div><div class='line' id='LC63'>	<span class="vi">@mock</span><span class="o">.</span><span class="n">threely</span> <span class="mi">333</span></div><div class='line' id='LC64'><span class="k">end</span></div><div class='line' id='LC65'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167235/5219e6cc11e24c648f8b4386aca4b0b4cc0ab80f/silly_mocking_spec.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167235#file_silly_mocking_spec.rb" style="float:right;margin-right:10px;color:#666">silly_mocking_spec.rb</a>
            <a href="https://gist.github.com/2167235">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>These are just ways I thought of to exercise the width and breadth of the library.  Very nice.  I hope that these are useful examples for people new to this gem.</p>
<p>I recommend the other references from above for filling in the missing details.  Once you have a <code>context / specify</code> or a <code>describe / it</code> specification set up, you&#8217;ll be good to go.  There&#8217;s much more to the library &#8212; Stories, for instance &#8212; but that&#8217;s <a href="http://blog.emson.co.uk/2008/06/understanding-rspec-stories-a-tutorial/">for another day</a>.</p>
<h3>An Informative Walk through Twitter4R</h3>
<p>No, I didn&#8217;t really <em>want</em> to spend time learning <code>rspec</code> &#8212; I mean heck, I&#8217;m busy &#8212; but I had a personal need to expand the <a href="http://twitter4r.rubyforge.org/"><strong>Twitter4R</strong></a> gem.  Specifically, I wanted to add on some <a href="http://apiwiki.twitter.com/Search+API+Documentation">Twitter search</a> features, and I was very impressed with how this library has been built.  Contribution-wise, the final step that <a href="http://susanpotter.net/">Susan Potter</a> recommends is to craft up some rspec tests.</p>
<p>Of course, mock testing is only as good as the framework you&#8217;re built upon.  The assumption is that <code>Net::HTTP</code> is going to do it&#8217;s job, so mock it up and you can even test your Twitter features offline.  When I built <a href="http://wiki.cantremember.com/Bitly4R"><strong>Bitly4R</strong></a> (given that name, my thinking has clearly been <em>influenced</em>), I did everything as full-on functional tests.  It was easy; <a href="http://bit.ly/">bit.ly</a> has both <code>shorten</code> and <code>expand</code> commands, so I could reverse-test real values without having any fixed expectations.</p>
<p>However, Twitter is <a href="http://twitter.com/public_timeline">live</a> and user-generated, so who knows <em>what</em> you&#8217;ll find.  Mocking covers that for you.  And of course not having to hit the service itself shortest testing time dramatically.</p>
<p>Here&#8217;s one of my tests, again foreshortened:</p>
<div id="gist-2167251" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><br/></div><div class='line' id='LC2'><span class="n">before</span><span class="p">(</span><span class="ss">:each</span><span class="p">)</span> <span class="k">do</span></div><div class='line' id='LC3'>	<span class="no">Twitter</span><span class="o">::</span><span class="no">Client</span><span class="o">.</span><span class="n">send</span> <span class="ss">:public</span><span class="p">,</span> <span class="ss">:create_http_get_request</span></div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'>	<span class="vi">@client</span> <span class="o">=</span> <span class="no">Twitter</span><span class="o">::</span><span class="no">Client</span><span class="o">.</span><span class="n">new</span></div><div class='line' id='LC6'>	<span class="vi">@client_clone</span> <span class="o">=</span> <span class="no">Twitter</span><span class="o">::</span><span class="no">Client</span><span class="o">.</span><span class="n">new</span></div><div class='line' id='LC7'><span class="k">end</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'><span class="n">it</span> <span class="s2">&quot;produces a querystring with all the crazy stuff Twitter imagined&quot;</span> <span class="k">do</span></div><div class='line' id='LC10'>	<span class="n">the_params</span> <span class="o">=</span> <span class="kp">nil</span></div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'>	<span class="vi">@client</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:create_http_get_request</span><span class="p">)</span><span class="o">.</span><span class="n">and_return</span> <span class="p">{</span><span class="o">|</span><span class="n">uri</span><span class="p">,</span> <span class="n">params</span><span class="o">|</span></div><div class='line' id='LC13'>		<span class="n">the_params</span> <span class="o">=</span> <span class="n">params</span></div><div class='line' id='LC14'>		<span class="vi">@client_clone</span><span class="o">.</span><span class="n">create_http_get_request</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span></div><div class='line' id='LC15'>	<span class="p">}</span></div><div class='line' id='LC16'>	<span class="vi">@client</span><span class="o">.</span><span class="n">should_receive</span><span class="p">(</span><span class="ss">:http_connect</span><span class="p">)</span><span class="o">.</span><span class="n">and_return</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTPNotFound</span><span class="o">.</span><span class="n">mock</span><span class="p">(</span><span class="ss">:code</span> <span class="o">=&gt;</span> <span class="s1">&#39;404&#39;</span><span class="p">)</span></div><div class='line' id='LC17'><br/></div><div class='line' id='LC18'>	<span class="c1">#	pass through a few we can pick back out</span></div><div class='line' id='LC19'>	<span class="c1">#	also, side-effect ... nil on invalid response</span></div><div class='line' id='LC20'>	<span class="vi">@client</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s1">&#39;query_value&#39;</span><span class="p">,</span> <span class="ss">:rpp</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">)</span><span class="o">.</span><span class="n">should</span> <span class="n">be_nil</span></div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'>	<span class="n">the_params</span><span class="o">.</span><span class="n">should_not</span> <span class="n">be_nil</span></div><div class='line' id='LC23'>	<span class="n">the_params</span><span class="o">[</span><span class="ss">:q</span><span class="o">].</span><span class="n">should</span> <span class="n">eql</span><span class="p">(</span><span class="s1">&#39;query_value&#39;</span><span class="p">)</span></div><div class='line' id='LC24'>	<span class="n">the_params</span><span class="o">[</span><span class="ss">:rpp</span><span class="o">].</span><span class="n">should</span> <span class="n">eql</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span></div><div class='line' id='LC25'><span class="k">end</span></div><div class='line' id='LC26'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167251/09866ae3ae9bd5bd4cf8ee247a6e3c3baf649ac2/twitter4r_mock_spec.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167251#file_twitter4r_mock_spec.rb" style="float:right;margin-right:10px;color:#666">twitter4r_mock_spec.rb</a>
            <a href="https://gist.github.com/2167251">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Plus a little mocking of <code>Net::HTTPResponse</code>:</p>
<div id="gist-2167255" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">Net</span><span class="o">::</span><span class="no">HTTPResponse</span></div><div class='line' id='LC2'>	<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">mock</span><span class="p">(</span><span class="n">ops</span><span class="o">=</span><span class="p">{})</span></div><div class='line' id='LC3'>		<span class="c1">#	defaults</span></div><div class='line' id='LC4'>		<span class="n">ops</span> <span class="o">=</span> <span class="p">{</span></div><div class='line' id='LC5'>			<span class="ss">:http_version</span> <span class="o">=&gt;</span> <span class="s1">&#39;1.1&#39;</span><span class="p">,</span></div><div class='line' id='LC6'>			<span class="ss">:code</span> <span class="o">=&gt;</span> <span class="s1">&#39;200&#39;</span><span class="p">,</span></div><div class='line' id='LC7'>			<span class="ss">:message</span> <span class="o">=&gt;</span> <span class="s1">&#39;OK&#39;</span><span class="p">,</span></div><div class='line' id='LC8'>			<span class="ss">:body</span> <span class="o">=&gt;</span> <span class="kp">nil</span><span class="p">,</span></div><div class='line' id='LC9'>		<span class="p">}</span><span class="o">.</span><span class="n">merge</span><span class="p">(</span><span class="n">ops</span> <span class="o">||</span> <span class="p">{})</span></div><div class='line' id='LC10'><br/></div><div class='line' id='LC11'>		<span class="c1">#	construct</span></div><div class='line' id='LC12'>		<span class="c1">###clazz = CODE_TO_OBJ[ops[:code]]</span></div><div class='line' id='LC13'>		<span class="n">clazz</span> <span class="o">=</span> <span class="nb">self</span></div><div class='line' id='LC14'>		<span class="n">response</span> <span class="o">=</span> <span class="n">clazz</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">ops</span><span class="o">[</span><span class="ss">:http_version</span><span class="o">]</span><span class="p">,</span> <span class="n">ops</span><span class="o">[</span><span class="ss">:code</span><span class="o">]</span><span class="p">,</span> <span class="n">ops</span><span class="o">[</span><span class="ss">:message</span><span class="o">]</span><span class="p">)</span></div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'>		<span class="c1">#	inject</span></div><div class='line' id='LC17'>		<span class="n">ops</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="o">|</span></div><div class='line' id='LC18'>			<span class="n">response</span><span class="o">.</span><span class="n">instance_variable_set</span> <span class="s2">&quot;@</span><span class="si">#{</span><span class="n">k</span><span class="si">}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">to_sym</span><span class="p">,</span> <span class="n">v</span></div><div class='line' id='LC19'>		<span class="k">end</span></div><div class='line' id='LC20'><br/></div><div class='line' id='LC21'>		<span class="c1">#	mockulate</span></div><div class='line' id='LC22'>		<span class="n">response</span><span class="o">.</span><span class="n">instance_eval</span> <span class="sx">%q{</span></div><div class='line' id='LC23'><span class="sx">			def body; @body; end</span></div><div class='line' id='LC24'><span class="sx">		}</span></div><div class='line' id='LC25'><br/></div><div class='line' id='LC26'>		<span class="n">response</span></div><div class='line' id='LC27'>	<span class="k">end</span></div><div class='line' id='LC28'><span class="k">end</span></div><div class='line' id='LC29'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167255/c3d2c265b5fdbe07318d3c94e14a6b70c2fd3b8b/net_http_response_mock.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167255#file_net_http_response_mock.rb" style="float:right;margin-right:10px;color:#666">net_http_response_mock.rb</a>
            <a href="https://gist.github.com/2167255">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Nothing like having some good inspiration to get you thinking about maintainability.  I&#8217;m a not a strong adherent to any of the <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a> or <a href="http://en.wikipedia.org/wiki/Behavior_driven_development">BDD</a> denominations, but testability itself is close to my heart.  Just ask the folks who&#8217;ll be looking at the micro-assertion Module that I wrote for my <a href="http://www.facebook.com/jobs_puzzles/">Facebook Puzzle</a> submissions (which work locally under test conditions but fail <em>miserably</em> once thrown over their wall).  Then again, I won&#8217;t need to write that (and test that) from scratch again.</p>
<p>So, back to talking glowingly about the <code>Twitter4R</code> architecture.  Yes, I used <strong>that</strong> word.  Yet regardless of that term&#8217;s heavyweightedness, it&#8217;s simply the result of carefully thinking out the formalization of a complex design.  And once a non-author delves into extending an existing implementation, that impl&#8217;s demeanor becomes readily clear :)</p>
<p>Some of the interesting things I saw:</p>
<ul>
<li>An inner <code>bless</code>-ish strategy, to inject the <code>Twitter::Client</code> into instanciated result classes.  I&#8217;ve seen blessing as an OO-Perl-ism, for self-ication, and that metaphor carries over very nicely to this context.</li>
<li>Generous use of blocks / lambdas / closures in methods, for contextual iteration (eg. iterate through each <code>Twitter::Message</code> in a <code>timeline</code> response).  Unnecessary, but an excellent convenience for a language that offers that optional capability in a very disposable fashion (from the caller&#8217;s perspective).</li>
<li>Retroactive sub-object population after construction.  <code>Twitter4R</code> relies upon the <a href="http://json.rubyforge.org/">json</a> gem, which deals primarily in Hashes.  Post-injection, the library itereates through Arrays of Hashes and transforms them into suitable <code>Twiltter::User</code> containers, etc.  A great place to put such logic in the construction chain, and it doesn&#8217;t take long to get really tired of Hash hierarchies.</li>
</ul>
<p>Good stuff, and learning with in a Ruby-centric mindset was invaluable for me.  We all have to start somewhere, eh.</p>
<h3>The Acute Long-Term Pain of Staticness</h3>
<p>There was one issue that I ran into; <em>static configuration</em>.  During my years of using the <a href="http://www.springsource.org/">Spring Framework</a>, my <a href="http://en.wikipedia.org/wiki/Inversion_of_Control">IOC</a>-addled brain started thinking of everything in terms of instances &#8212; Factory Beans, POJOs, Observers &#038; Listeners, Chain-of-Responsibility wrappers.  Static configuration is a common metaphor, and in this case, there&#8217;s a static <code>Twitter::Config</code> instance.  Convenient and centralized.  Makes perfect sense.</p>
<p>I mean, the fact that it was a configurable library <em>at all</em> was awesome.  I was able to easily set up a <code>Twitter::Client</code> to reference <a href="http://search.twitter.com">search.twitter.com</a>.  However, of course as soon as I did that, I whacked the ability for clients to talk to <code>twitter.com</code> in the process.  <em>Oops!</em></p>
<p>On GPs, I refused to modify the original code.  And I wanted to make sure that my superficial tweaks to the library would be thread-safe &#8212; temporarily swaping out the global <code>Twitter::Config</code> in mid-operation would be an issue.  Using <code>Mutex.synchronize</code> seemed like the perfect choice.  After finding that the same thread can&#8217;t lock a <code>Mutex</code> instance twice &#8212; grr!, that&#8217;s a great trick if you can work it &#8212; I overrode the one method that cared the most about <code>@@config</code>:</p>
<div id="gist-2167260" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="vc">@@config_mutex</span> <span class="o">=</span> <span class="no">Mutex</span><span class="o">.</span><span class="n">new</span></div><div class='line' id='LC2'><span class="k">def</span> <span class="nf">configuration_mutex</span></div><div class='line' id='LC3'>	<span class="vc">@@config_mutex</span></div><div class='line' id='LC4'><span class="k">end</span></div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'><span class="k">alias</span> <span class="ss">:raw_http_connect</span> <span class="ss">:http_connect</span></div><div class='line' id='LC7'><span class="k">def</span> <span class="nf">http_connect</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span></div><div class='line' id='LC8'>	<span class="n">ops</span> <span class="o">=</span> <span class="p">(</span><span class="no">Hash</span> <span class="o">===</span> <span class="n">args</span><span class="o">.</span><span class="n">last</span><span class="p">)</span> <span class="p">?</span> <span class="n">args</span><span class="o">.</span><span class="n">pop</span> <span class="p">:</span> <span class="p">{}</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>	<span class="n">result</span> <span class="o">=</span> <span class="kp">nil</span></div><div class='line' id='LC11'>	<span class="nb">self</span><span class="o">.</span><span class="n">configuration_mutex</span><span class="o">.</span><span class="n">synchronize</span> <span class="k">do</span></div><div class='line' id='LC12'>		<span class="n">ops</span><span class="o">[</span><span class="ss">:call_before</span><span class="o">].</span><span class="n">call</span> <span class="k">if</span> <span class="n">ops</span><span class="o">[</span><span class="ss">:call_before</span><span class="o">]</span></div><div class='line' id='LC13'>		<span class="n">result</span> <span class="o">=</span> <span class="n">raw_http_connect</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span></div><div class='line' id='LC14'>		<span class="n">ops</span><span class="o">[</span><span class="ss">:call_after</span><span class="o">].</span><span class="n">call</span> <span class="k">if</span> <span class="n">ops</span><span class="o">[</span><span class="ss">:call_after</span><span class="o">]</span></div><div class='line' id='LC15'>	<span class="k">end</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'>	<span class="n">result</span></div><div class='line' id='LC18'><span class="k">end</span></div><div class='line' id='LC19'><br/></div><div class='line' id='LC20'><span class="c1">#</span></div><div class='line' id='LC21'><span class="c1">#	...</span></div><div class='line' id='LC22'><span class="c1">#</span></div><div class='line' id='LC23'><br/></div><div class='line' id='LC24'><span class="vc">@@SEARCH_CONNECT_HOOKS</span> <span class="o">=</span> <span class="p">{</span></div><div class='line' id='LC25'>	<span class="ss">:call_before</span> <span class="o">=&gt;</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="nb">self</span><span class="o">.</span><span class="n">configure_apply</span><span class="p">(</span><span class="ss">:search</span><span class="p">)</span> <span class="p">},</span></div><div class='line' id='LC26'>	<span class="ss">:call_after</span> <span class="o">=&gt;</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="nb">self</span><span class="o">.</span><span class="n">configure_apply</span><span class="p">(</span><span class="ss">:default</span><span class="p">)</span> <span class="p">},</span></div><div class='line' id='LC27'><span class="p">}</span></div><div class='line' id='LC28'><br/></div><div class='line' id='LC29'><span class="c1">#</span></div><div class='line' id='LC30'><span class="c1">#	...</span></div><div class='line' id='LC31'><span class="c1">#</span></div><div class='line' id='LC32'><br/></div><div class='line' id='LC33'><span class="c1">#	broken out of closure for mocking</span></div><div class='line' id='LC34'><span class="n">req</span> <span class="o">=</span> <span class="n">create_http_get_request</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span></div><div class='line' id='LC35'><span class="n">response</span> <span class="o">=</span> <span class="n">http_connect</span><span class="p">(</span><span class="vc">@@SEARCH_CONNECT_HOOKS</span><span class="p">)</span> <span class="p">{</span><span class="o">|</span><span class="n">conn</span><span class="o">|</span>	<span class="n">req</span> <span class="p">}</span></div><div class='line' id='LC36'><span class="k">return</span> <span class="kp">nil</span> <span class="k">unless</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTPOK</span> <span class="o">===</span> <span class="n">response</span></div><div class='line' id='LC37'><br/></div><div class='line' id='LC38'><span class="n">model</span> <span class="o">=</span> <span class="n">bless_model</span><span class="p">(</span><span class="no">Twitter</span><span class="o">::</span><span class="no">Search</span><span class="o">.</span><span class="n">unmarshal</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">))</span></div><div class='line' id='LC39'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167260/7a7657463d826779efbdfb3b55ea1aa65c7dfb3f/twitter4r_search_patch.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167260#file_twitter4r_search_patch.rb" style="float:right;margin-right:10px;color:#666">twitter4r_search_patch.rb</a>
            <a href="https://gist.github.com/2167260">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>It works like a charm.  Please, everyone just line up to tell me how I could have done it better.  And I don&#8217;t mean <em>quicker</em> or <em>cheaper</em>, I mean better.  Believe me, I would not have sunk the time into this end-around approach, if not for the fact that:</p>
<ol>
<li>I don&#8217;t want to maintain a local gem code modification (even though my impl is closely coupled to the gem impl already)</li>
<li>I intend to follow that practice, so every opportunity to pull and end-around is a Valuable Learning Experience.</li>
</ol>
<p>So, now my local <code>Twitter4R</code> has search capability gracefully latched onto it (and implemented much in the flavor of the library itself).  I have a mass of <code>rspec</code> examples to work off in the future.</p>
<p>Now, I haven&#8217;t spent a great amount of time testing the thread-safeness &#8212; no one in their right mind <em>wants</em> to do that &#8212; but my sundry <code>Twitter::Client</code> instances play nicely together in between searches and normal status operations.</p>
<p>And I had something useful to blog about!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/twitter4r-shifts-rspec-onto-my-front-burner/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Remote Scripting for AWS</title>
		<link>http://blog.cantremember.com/remote-scripting-for-aws/</link>
		<comments>http://blog.cantremember.com/remote-scripting-for-aws/#comments</comments>
		<pubDate>Sat, 24 Jan 2009 20:36:01 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=73</guid>
		<description><![CDATA[When signing up for Amazon Web Services, you end up generating all of the following identifying information: Your Account Number An Access Key A Secret Access Key An SSL Certificate file The Private Key for your SSL Cert The various AWS command APIs and tools will require you to provide one more more of these [...]]]></description>
			<content:encoded><![CDATA[<p>When signing up for <a href="http://aws.amazon.com">Amazon Web Services</a>, you end up generating all of the following identifying information:</p>
<ul>
<li>Your Account Number</li>
<li>An Access Key</li>
<li>A Secret Access Key</li>
<li>An SSL Certificate file</li>
<li>The Private Key for your SSL Cert</li>
</ul>
<p>The various AWS command APIs and tools will require you to provide one more more of these pieces of information.  Rather than actually host all of this information on my instances, I have instead chosen to build Ruby scripts using:</p>
<ul>
<li>Glenn Rempe&#8217;s Amazon EC2 gem (<a href="http://rubyforge.org/projects/amazon-ec2">amazon-ec2</a>)</li>
<li>The <a href="http://rubyforge.org/projects/net-ssh">net-ssh</a> and net-scp gem suite</li>
</ul>
<p>I&#8217;m small-scale for the moment, so I can keep a centralized list of my instance IDs.  Without too much effort, I can look up the instance(s), identify their public DNS (plus everything else), and then open up an ssh connection and push data into a channel&#8217;s environment or upload files for bundling purposes.</p>
<p>Here&#8217;s a few things that I&#8217;ve learned in the process.</p>
<h3>Channels</h3>
<p>Most of your core work gets done on a <code>Net::SSH::Connection::Channel</code>, and sometimes asynchronously (as in the case of my Health Check script).  There are a lot of library shorthands &#8212; the prototypical Hello World example is channel-less:</p>
<div class="pre_wrap">
<pre><code>Net::SSH.start("localhost", "user") do |ssh|
	# 'ssh' is an instance of Net::SSH::Connection::Session
	ssh.exec! "echo Hello World"
end</code></pre>
</div>
<p>But as with all examples, you soon find that surface-level tools won&#8217;t quite do the trick.  The main thing you&#8217;ll need to have all the time is convenient access to the data come back from the channel, asynchronously or otherwise.  So create yourself a simple observer:</p>
<div id="gist-2167314" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="k">def</span> <span class="nf">observer_struct</span></div><div class='line' id='LC2'>	<span class="vi">@observer_struct</span> <span class="o">||=</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:channel</span><span class="p">,</span> <span class="ss">:stdout</span><span class="p">,</span> <span class="ss">:stderr</span><span class="p">,</span> <span class="ss">:exit_status</span><span class="p">,</span> <span class="ss">:exit_signal</span><span class="p">)</span></div><div class='line' id='LC3'><span class="k">end</span></div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'><span class="k">def</span> <span class="nf">observe_channel</span><span class="p">(</span><span class="n">ch</span><span class="p">,</span> <span class="n">ops</span><span class="o">=</span><span class="p">{})</span></div><div class='line' id='LC6'>	<span class="n">observer</span> <span class="o">=</span> <span class="n">observer_struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">ch</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kp">nil</span><span class="p">)</span></div><div class='line' id='LC7'>	<span class="n">is_puts</span> <span class="o">=</span> <span class="o">!!</span><span class="n">ops</span><span class="o">[</span><span class="ss">:puts</span><span class="o">]</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'>	<span class="c1">#	stdout</span></div><div class='line' id='LC10'>	<span class="n">ch</span><span class="o">.</span><span class="n">on_data</span> <span class="k">do</span> <span class="o">|</span><span class="n">ch</span><span class="p">,</span> <span class="n">data</span><span class="o">|</span></div><div class='line' id='LC11'>		<span class="vg">$stdout</span><span class="o">.</span><span class="n">print</span> <span class="n">data</span> <span class="k">if</span> <span class="n">is_puts</span></div><div class='line' id='LC12'>		<span class="n">observer</span><span class="o">.</span><span class="n">stdout</span> <span class="o">&amp;</span><span class="n">lt</span><span class="p">;</span><span class="o">&amp;</span><span class="n">lt</span><span class="p">;</span> <span class="n">data</span></div><div class='line' id='LC13'>	<span class="k">end</span></div><div class='line' id='LC14'>	<span class="c1"># only handle stderr</span></div><div class='line' id='LC15'>	<span class="n">ch</span><span class="o">.</span><span class="n">on_extended_data</span> <span class="k">do</span> <span class="o">|</span><span class="n">ch</span><span class="p">,</span> <span class="n">type</span><span class="p">,</span> <span class="n">data</span><span class="o">|</span></div><div class='line' id='LC16'>		<span class="k">next</span> <span class="k">unless</span> <span class="o">[</span><span class="mi">1</span><span class="p">,</span> <span class="ss">:stderr</span><span class="o">].</span><span class="n">include?</span> <span class="n">type</span></div><div class='line' id='LC17'>		<span class="vg">$stderr</span><span class="o">.</span><span class="n">print</span> <span class="n">data</span> <span class="k">if</span> <span class="n">is_puts</span></div><div class='line' id='LC18'>		<span class="n">observer</span><span class="o">.</span><span class="n">stderr</span> <span class="o">&amp;</span><span class="n">lt</span><span class="p">;</span><span class="o">&amp;</span><span class="n">lt</span><span class="p">;</span> <span class="n">data</span></div><div class='line' id='LC19'>	<span class="k">end</span></div><div class='line' id='LC20'><br/></div><div class='line' id='LC21'>	<span class="n">ch</span><span class="o">.</span><span class="n">on_request</span><span class="p">(</span><span class="s2">&quot;exit-status&quot;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">ch</span><span class="p">,</span> <span class="n">data</span><span class="o">|</span></div><div class='line' id='LC22'>		<span class="n">observer</span><span class="o">.</span><span class="n">exit_status</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">read_long</span></div><div class='line' id='LC23'>	<span class="k">end</span></div><div class='line' id='LC24'>	<span class="n">ch</span><span class="o">.</span><span class="n">on_request</span><span class="p">(</span><span class="s2">&quot;exit-signal&quot;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">ch</span><span class="p">,</span> <span class="n">data</span><span class="o">|</span></div><div class='line' id='LC25'>		<span class="n">observer</span><span class="o">.</span><span class="n">exit_signal</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">read_long</span></div><div class='line' id='LC26'>	<span class="k">end</span></div><div class='line' id='LC27'><br/></div><div class='line' id='LC28'>	<span class="n">observer</span></div><div class='line' id='LC29'><span class="k">end</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'><span class="k">def</span> <span class="nf">open_channel</span><span class="p">(</span><span class="n">ssh</span><span class="p">,</span> <span class="n">ops</span><span class="o">=</span><span class="p">{},</span> <span class="o">&amp;</span><span class="n">amp</span><span class="p">;</span><span class="n">block</span><span class="p">)</span></div><div class='line' id='LC32'>	<span class="n">ch</span> <span class="o">=</span> <span class="k">if</span> <span class="nb">block_given?</span></div><div class='line' id='LC33'>		<span class="n">ssh</span><span class="o">.</span><span class="n">open_channel</span> <span class="o">&amp;</span><span class="n">amp</span><span class="p">;</span><span class="n">block</span></div><div class='line' id='LC34'>	<span class="k">else</span></div><div class='line' id='LC35'>		<span class="n">ssh</span><span class="o">.</span><span class="n">open_channel</span></div><div class='line' id='LC36'>	<span class="k">end</span></div><div class='line' id='LC37'><br/></div><div class='line' id='LC38'>	<span class="c1">#	somebody&#39;s watching you</span></div><div class='line' id='LC39'>	<span class="nb">self</span><span class="o">.</span><span class="n">observe_channel</span> <span class="n">ch</span><span class="p">,</span> <span class="n">ops</span></div><div class='line' id='LC40'><span class="k">end</span></div><div class='line' id='LC41'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167314/fbcec90c7f2bf2835a8f18444536623141218a28/net_ssh_channel_observer.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167314#file_net_ssh_channel_observer.rb" style="float:right;margin-right:10px;color:#666">net_ssh_channel_observer.rb</a>
            <a href="https://gist.github.com/2167314">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Then during and after executions, you&#8217;ll have access to all the core information you&#8217;ll need to make conditional decisions, etc.</p>
<h3>Pushing Your Environment</h3>
<p>Many of the AWS tools will recognize specific environment variables.  The usual suspects are:</p>
<ul>
<li>AMAZON_ACCESS_KEY_ID</li>
<li>AMAZON_SECRET_ACCESS_KEY</li>
</ul>
<p>So, you can use <code>Net::SSH::Connection::Channel.<strong>env</strong></code> to inject key-value pairs into your connection.  However, you&#8217;ll need to make some config changes first &#8212; and <em>major thanks</em> to the Net::SSH documentation for clarifying this in its own RDoc:</p>
<blockquote><p>&#8220;Modify <code>/etc/ssh/sshd_config</code> so that it includes an AcceptEnv for each variable you intend to push over the wire&#8221;
</p></blockquote>
<div class="pre_wrap">
<pre>#       accept EC2 config
AcceptEnv  AMAZON_ACCESS_KEY_ID AMAZON_SECRET_ACCESS_KEY
...</pre>
</div>
<p>After you make your changes, make sure to bounce the ssh daemon:</p>
<div class="pre_wrap">
<pre><code>/etc/init.d/sshd restart</code></pre>
</div>
<p>Then your selected environment will shine through.</p>
<h3>Running sudo Remotely</h3>
<p>It&#8217;s reasonable to execute root-level commands using sudo, because you can provide a good amount of granular control.  </p>
<p>The first thing that we&#8217;ll all run into is:</p>
<div class="pre_wrap">
<pre>sudo: sorry, you must have a tty to run sudo</pre>
</div>
<p>Fortunately, there&#8217;s <code>Net::SSH::Connection::Channel.<strong>request_pty</strong></code>:</p>
<div class="pre_wrap">
<pre><code>ch.request_pty do |ch, success|
	raise "could not start a pseudo-tty" unless success

	#	full EC2 environment
	###ch.env 'key', 'value'
	###...

	ch.exec 'sudo echo Hello 1337' do |ch, success|
		raise "could not exec against a pseudo-tty" unless success
	end
end</code></pre>
</div>
<p>I&#8217;ve taken the approach of allowing <code>NOPASSWD</code> execution for everything I do remotely, after making darn sure that I had constrained exactly what could be done under those auspices (HINT: pounds of caution, tons of prevention).  You can configure all of this by editing <code>/etc/sudoers</code>:</p>
<div class="pre_wrap">
<pre># EC2 tool permissions
username  ALL=(ALL)  NOPASSWD: SETENV: /path/to/scripts/*.rb
...</pre>
</div>
<p>Also, if you intend to have those scripts consume the environment variables that you&#8217;ve injected into your channel, you&#8217;ll also need to annotate the line with <code>SETENV</code>, or they won&#8217;t export across into your sudo execution.</p>
<p>If you are more security minded, then there&#8217;s many other variations you can play with <a href="http://www.manpagez.com/man/5/sudoers/"><code>/etc/sudoers</code></a>.  I haven&#8217;t yet experimented within pushing a sudo password across the wire, but that may be where <code>Net::SSH::Connection::Channel.<strong>send_data</strong></code> comes into play.</p>
<h3>Port Forwarding</h3>
<p>I wanted to easily do SSH tunneling / port-forwarding against an instance, referenced by my local shorthand.  So I wrote my usual EC2 instance ID lookup and started an Net::SSH connection.  Here&#8217;s how you put yourself in &#8216;dumb forward&#8217; mode:</p>
<div id="gist-2167326" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="k">def</span> <span class="nf">ports</span><span class="p">,</span> <span class="n">offset</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="mi">0</span></div><div class='line' id='LC2'><span class="c1"># ...</span></div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'><span class="no">Net</span><span class="o">::</span><span class="no">SSH</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="n">hostname</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="ss">:forward_agent</span> <span class="o">=&amp;</span><span class="n">gt</span><span class="p">;</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">:verbose</span> <span class="o">=&amp;</span><span class="n">gt</span><span class="p">;</span> <span class="ss">:warn</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">ssh</span><span class="o">|</span></div><div class='line' id='LC5'>	<span class="n">ports</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">port</span><span class="o">|</span></div><div class='line' id='LC6'>		<span class="n">ssh</span><span class="o">.</span><span class="n">forward</span><span class="o">.</span><span class="n">local</span> <span class="n">port</span> <span class="o">+</span> <span class="n">offset</span><span class="p">,</span> <span class="s1">&#39;localhost&#39;</span><span class="p">,</span> <span class="n">port</span></div><div class='line' id='LC7'>	<span class="k">end</span></div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'>	<span class="c1"># loop, with an event loop every 0.1s, until Ctrl-C is pressed</span></div><div class='line' id='LC10'>	<span class="nb">puts</span> <span class="s2">&quot;[Ctrl-C] to terminate...&quot;</span></div><div class='line' id='LC11'>	<span class="n">interrupted</span> <span class="o">=</span> <span class="kp">false</span></div><div class='line' id='LC12'>	<span class="nb">trap</span><span class="p">(</span><span class="s1">&#39;INT&#39;</span><span class="p">)</span> <span class="p">{</span> <span class="n">interrupted</span> <span class="o">=</span> <span class="kp">true</span> <span class="p">}</span></div><div class='line' id='LC13'>	<span class="n">ssh</span><span class="o">.</span><span class="n">loop</span><span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span></div><div class='line' id='LC14'>		<span class="ow">not</span> <span class="n">interrupted</span></div><div class='line' id='LC15'>	<span class="p">}</span></div><div class='line' id='LC16'><br/></div><div class='line' id='LC17'>	<span class="n">ports</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">port</span><span class="o">|</span></div><div class='line' id='LC18'>		<span class="n">ssh</span><span class="o">.</span><span class="n">forward</span><span class="o">.</span><span class="n">cancel_local</span> <span class="n">port</span> <span class="o">+</span> <span class="n">offset</span></div><div class='line' id='LC19'>	<span class="k">end</span></div><div class='line' id='LC20'><span class="k">end</span></div><div class='line' id='LC21'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167326/94513ad171c3e62e494561a520a7ce6ebe4ed45f/net_ssh_dumb_forward.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167326#file_net_ssh_dumb_forward.rb" style="float:right;margin-right:10px;color:#666">net_ssh_dumb_forward.rb</a>
            <a href="https://gist.github.com/2167326">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>And then <code>[Ctrl-C]</code> will break you out.</p>
<h3>Inconsistent EC2 Marshalling</h3>
<p>I&#8217;d gotten all of my EC2 data parsing working great, then I started to expand my capabilities with the Amazon S3 API gem (<a href="http://rubyforge.org/projects/amazon/">aws-s3</a>).  Suddenly and magically, the results coming back from my EC2 queries had taken on a new form.  Moreover, this was <em>not consistent behavior</em> across all operating systems; at least I do not see it on WinXP, under which my health monitoring checks execute.</p>
<p>The Amazon APIs are SOAP-driven, so I <em>guessed</em> that amazon-ec2 (and aws-3) would both leverage <a href="http://rubyforge.org/projects/soap4r">soap4r</a>.  Well, that is in fact <em>not</em> the case; Glenn has commented below on this known issue.  He uses the HTTP Query API, and the cause of the discrepancy are (as of this writing) still undetermined.</p>
<p>Regardless, our job is to get things to work properly.  So for the meanwhile, let&#8217;s use a quick &#038; dirty work-around.  The two core differences are:</p>
<ul>
<li>the data coming back may be wrapped in an additional key-value pair</li>
<li>single value result-sets come back as an Object (vs. an Array with a single Object)</li>
</ul>
<p>So I crafted a little helper singleton, which which still allows me to detect between &#8216;valid&#8217; and &#8216;empty&#8217; requests (<code>nil</code> vs <code>[]</code>):</p>
<div id="gist-2167338" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="k">class</span> <span class="nc">SOAPHelper</span></div><div class='line' id='LC2'>	<span class="k">class</span> <span class="o">&amp;</span><span class="n">lt</span><span class="p">;</span><span class="o">&amp;</span><span class="n">lt</span><span class="p">;</span> <span class="nb">self</span></div><div class='line' id='LC3'>		<span class="k">def</span> <span class="nf">data_from</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">wrapper</span><span class="p">)</span></div><div class='line' id='LC4'>			<span class="n">data</span><span class="o">[</span><span class="n">wrapper</span><span class="o">]</span> <span class="o">||</span> <span class="n">data</span></div><div class='line' id='LC5'>		<span class="k">end</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'>		<span class="k">def</span> <span class="nf">items_from</span><span class="p">(</span><span class="n">set</span><span class="p">)</span></div><div class='line' id='LC8'>			<span class="k">unless</span> <span class="n">set</span> <span class="o">&amp;</span><span class="n">amp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">amp</span><span class="p">;</span> <span class="n">set</span><span class="o">.</span><span class="n">item</span></div><div class='line' id='LC9'>				<span class="kp">nil</span></div><div class='line' id='LC10'>			<span class="k">else</span></div><div class='line' id='LC11'>				<span class="n">items</span> <span class="o">=</span> <span class="n">set</span><span class="o">.</span><span class="n">item</span></div><div class='line' id='LC12'>				<span class="n">items</span> <span class="o">=</span> <span class="o">[</span><span class="n">items</span><span class="o">]</span> <span class="k">unless</span> <span class="nb">Array</span> <span class="o">===</span> <span class="n">items</span></div><div class='line' id='LC13'>				<span class="n">items</span></div><div class='line' id='LC14'>			<span class="k">end</span></div><div class='line' id='LC15'>		<span class="k">end</span></div><div class='line' id='LC16'>	<span class="k">end</span></div><div class='line' id='LC17'><span class="k">end</span></div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'><span class="n">data</span> <span class="o">=</span> <span class="no">SOAPHelper</span><span class="o">.</span><span class="n">data_from</span><span class="p">(</span></div><div class='line' id='LC20'>	<span class="vi">@ec2</span><span class="o">.</span><span class="n">describe_instances</span><span class="p">,</span></div><div class='line' id='LC21'>	<span class="s1">&#39;DescribeInstancesResponse&#39;</span></div><div class='line' id='LC22'><span class="p">)</span></div><div class='line' id='LC23'><span class="n">reservations</span> <span class="o">=</span> <span class="no">SOAPHelper</span><span class="o">.</span><span class="n">items_from</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">reservationSet</span><span class="p">)</span> <span class="o">||</span> <span class="o">[]</span></div><div class='line' id='LC24'><span class="o">.</span><span class="n">.</span><span class="o">.</span></div><div class='line' id='LC25'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167338/a30bee8faa11e3d669427f44ec0484d68408a5ce/soap_helper_for_amazon-ec2.rb" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167338#file_soap_helper_for_amazon_ec2.rb" style="float:right;margin-right:10px;color:#666">soap_helper_for_amazon-ec2.rb</a>
            <a href="https://gist.github.com/2167338">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>That will address our immediate needs for the moment, until a benificent coding Samaritan comes along.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/remote-scripting-for-aws/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Easy delivery with mstmp and GMail</title>
		<link>http://blog.cantremember.com/easy-delivery-with-mstmp-and-gmail/</link>
		<comments>http://blog.cantremember.com/easy-delivery-with-mstmp-and-gmail/#comments</comments>
		<pubDate>Sat, 24 Jan 2009 02:00:57 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Site]]></category>
		<category><![CDATA[gmail]]></category>
		<category><![CDATA[msmtp]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=64</guid>
		<description><![CDATA[At the moment, I really don&#8217;t feel like setting up a full-fledged MTA such as sendmail, postfix or qmail. I want to take the simple course, basically because I&#8217;m lazy. Fortunately, there are a variety of simple SMTP &#8216;relays&#8217; out there such as ssmtp and esmtp. Some network officianatos may consider this to be re-inventing [...]]]></description>
			<content:encoded><![CDATA[<p>At the moment, I really don&#8217;t feel like setting up a full-fledged MTA such as <a href="http://www.sendmail.org/">sendmail</a>, <a href="http://www.postfix.org/">postfix</a> or <a href="http://www.qmail.org/top.html">qmail</a>.  I want to take the simple course, basically because I&#8217;m lazy.  Fortunately, there are a variety of simple SMTP &#8216;relays&#8217; out there such as <a href="http://packages.debian.org/unstable/mail/ssmtp">ssmtp</a> and <a href="http://esmtp.sourceforge.net/">esmtp</a>.  Some network officianatos may consider this to be re-inventing the wheel, but then again, I&#8217;m sure glad that my car doesn&#8217;t roll on stone cylinders.</p>
<p>After some consideration, I chose to go with <a href="http://msmtp.sourceforge.net/"><strong>msmtp</strong></a>.  I like its flexible configuration, and it&#8217;s just the right size for the job (with room to grow).  The major thing I was looking for was STARTTLS support.  I wasn&#8217;t so concerned with the trust files and certificates, I just had a need to support GMail&#8217;s minimum requirements.  Yes, msmtp gives you that <em>and</em> the whole 9 yards, for when I need them all.</p>
<h3>msmtp Configuration for GMail</h3>
<p>With a combination of their official <a href="http://msmtp.sourceforge.net/doc/msmtprc.txt">configuration example</a> plus a few targeted suggestions from <a href="http://greybeardedgeek.net/?p=17">Grey Bearded Geek&#8217;s take at ssmtp</a>, I came up with the following:</p>
<div id="gist-2167372" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'># Set default values for all following accounts.</div><div class='line' id='LC2'>defaults</div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'>host  smtp.gmail.com</div><div class='line' id='LC5'>port  587</div><div class='line' id='LC6'>timeout  off</div><div class='line' id='LC7'>protocol  smtp</div><div class='line' id='LC8'>domain  localhost</div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'># /usr/bin/msmtp --version</div><div class='line' id='LC11'>#  plain cram-md5 digest-md5 gssapi external login ntlm</div><div class='line' id='LC12'>auth  on</div><div class='line' id='LC13'>user  GMAIL-USER</div><div class='line' id='LC14'>password  GMAIL-PASSWD</div><div class='line' id='LC15'><br/></div><div class='line' id='LC16'>tls  on</div><div class='line' id='LC17'>tls_starttls  on</div><div class='line' id='LC18'># WARNING: When the checks are disabled, TLS/SSL sessions will be vulnerable to man-in-the-middle attacks</div><div class='line' id='LC19'>tls_certcheck  off</div><div class='line' id='LC20'><br/></div><div class='line' id='LC21'>logfile  /var/log/msmtp.log</div><div class='line' id='LC22'>###syslog  on</div><div class='line' id='LC23'><br/></div><div class='line' id='LC24'># A system wide configuration is optional.</div><div class='line' id='LC25'># If it exists, it usually defines a default account.</div><div class='line' id='LC26'># This allows msmtp to be used like /usr/sbin/sendmail.</div><div class='line' id='LC27'>account default</div><div class='line' id='LC28'><br/></div><div class='line' id='LC29'># Construct envelope-from addresses of the form &quot;user@oursite.example&quot;.</div><div class='line' id='LC30'>###auto_from on</div><div class='line' id='LC31'>from  USER@DOMAIN</div><div class='line' id='LC32'>maildomain  DOMAIN</div><div class='line' id='LC33'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167372/02edcc558a7e12eb4f97c8c2a053dfc497f79c45/msmtp_gmail.conf" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167372#file_msmtp_gmail.conf" style="float:right;margin-right:10px;color:#666">msmtp_gmail.conf</a>
            <a href="https://gist.github.com/2167372">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>You plop in the <code>GMAIL-USER</code> and <code>GMAIL-PASSWD</code>, and you&#8217;re good to go.</p>
<h3>Custom From: Address</h3>
<p>I soon learned that the <code>from</code> and <code>maildomain</code> settings are irrelevant; Google <a href="http://stackoverflow.com/questions/109520/rails-and-gmail-smtp-how-to-use-a-custom-from-address">will not arbitrarily change</a> the <code>From:</code> header of your mail.  That makes sense.  So the mail will appear as if it&#8217;s coming from you, <em>personally</em>.  Well, it turns out that there&#8217;s a few things you can do to get around that.</p>
<ul>
<li>Create yourself a <em>dedicated GMail account</em>.  Now you have isolated your soon-to-be-wildly-popular start-up&#8217;s e-mail account from your personal one.</li>
<li>Follow the instructions on <a href="http://mail.google.com/support/bin/answer.py?ctx=gmail&amp;hl=en&amp;answer=22370">adding a custom From: address to your account</a>.  I had to use the older version of the GMail interface to do so.  Google will verify that you own the address &#8212; you&#8217;d better be able to receive mail at that address &#8212; and then you can make it your default.
<p>GMail will now send your mail as it were coming from that address, but it will do so without providing an alias.</li>
<li>When sending your outbound mail, you can include the following headers:
<div class="pre_wrap">
<pre><code>From:  <strong>ALIAS</strong>
Reply-To:  <strong>ALIAS &lt;USER@DOMAIN</strong>&gt;</code></pre>
</div>
<p>Google will respect the <code>ALIAS</code> portion of the <code>From:</code> address, though not the address <em>itself</em>.  The <code>Reply-To:</code> is optional, but respected in its entirety (alias and address).</li>
</ul>
<p>Works like a charm.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/easy-delivery-with-mstmp-and-gmail/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress and PmWiki under nginx</title>
		<link>http://blog.cantremember.com/wordpress-and-pmwiki-under-nginx/</link>
		<comments>http://blog.cantremember.com/wordpress-and-pmwiki-under-nginx/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 21:38:00 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Site]]></category>
		<category><![CDATA[fcgi]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[pmwiki]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=57</guid>
		<description><![CDATA[I recently double-checked my nginx configuration against the one that Elastic Dog has so proudly featured. I&#8217;m very glad that I did &#8212; they provided me with a better understanding of the if / test capabilities of the syntax. That being said, it still needed some adjustments &#8230; WordPress I&#8217;m currently running WordPress 2.7 under [...]]]></description>
			<content:encoded><![CDATA[<p>I recently double-checked my <a href="http://wiki.codemongers.com/Main">nginx</a> configuration against the one that <a href="http://elasticdog.com/2008/02/howto-install-wordpress-on-nginx/">Elastic Dog</a> has so proudly featured.  I&#8217;m very glad that I did &#8212; they provided me with a better understanding of the if / test capabilities of the syntax.</p>
<p>That being said, it still needed some adjustments &#8230;</p>
<h3>WordPress</h3>
<p>I&#8217;m currently running <a href="http://wordpress.com">WordPress</a> 2.7 under nginx 0.7.27.  Here&#8217;s my end configuration:</p>
<div id="gist-2167455" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><br/></div><div class='line' id='LC2'>server {</div><div class='line' id='LC3'>	include  extra/proxy.conf;</div><div class='line' id='LC4'>	include  mime.types;</div><div class='line' id='LC5'><br/></div><div class='line' id='LC6'>	listen  80;</div><div class='line' id='LC7'>	server_name  blog.DOMAIN.NAME;</div><div class='line' id='LC8'><br/></div><div class='line' id='LC9'>	access_log  logs/LOGFILE-access.log;</div><div class='line' id='LC10'>	###error_log  logs/LOGFILE-error.log  debug;</div><div class='line' id='LC11'>	error_log  logs/LOGFILE-error.log;</div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'>	# Settings | General | WordPress address (URL)</div><div class='line' id='LC14'>	location /WP-CONTEXT {</div><div class='line' id='LC15'>		# physical location on server</div><div class='line' id='LC16'>		root  /PATH/TO/WP-DIR;</div><div class='line' id='LC17'>		index  index.php index.html index.htm;</div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'>		# remove virtual path, make it root-relative</div><div class='line' id='LC20'>		rewrite  ^/WP-CONTEXT(.*)$  $1;</div><div class='line' id='LC21'><br/></div><div class='line' id='LC22'>		# this serves static files that exist without running other rewrite tests</div><div class='line' id='LC23'>		if (-f $request_filename) {</div><div class='line' id='LC24'>			expires 30d;</div><div class='line' id='LC25'>			break;</div><div class='line' id='LC26'>		}</div><div class='line' id='LC27'><br/></div><div class='line' id='LC28'>		# addresses &#39;/wp-admin/&#39; and similar vein</div><div class='line' id='LC29'>		if (-f $request_filename/index.php) {</div><div class='line' id='LC30'>			# produces &#39;...//index.php&#39;, acceptable loss</div><div class='line' id='LC31'>			rewrite  ^(.+)$  /WP-CONTEXT$1/index.php  last;</div><div class='line' id='LC32'>		}</div><div class='line' id='LC33'><br/></div><div class='line' id='LC34'>		# this sends all non-existing file or directory requests to index.php</div><div class='line' id='LC35'>		if (!-e $request_filename) {</div><div class='line' id='LC36'>			rewrite  ^(.+)$  /WP-CONTEXT/index.php?q=$1  last;</div><div class='line' id='LC37'>		}</div><div class='line' id='LC38'>	}</div><div class='line' id='LC39'><br/></div><div class='line' id='LC40'>	location ~ \.php$ {</div><div class='line' id='LC41'>		# remove virtual path, make it root-relative</div><div class='line' id='LC42'>		rewrite  ^/WP-CONTEXT(.*)$  $1;</div><div class='line' id='LC43'><br/></div><div class='line' id='LC44'>		fastcgi_pass  fastcgi_cluster;</div><div class='line' id='LC45'>		fastcgi_index index.php;</div><div class='line' id='LC46'>		# physical location on server</div><div class='line' id='LC47'>		fastcgi_param SCRIPT_FILENAME /PATH/TO/WP-DIR$fastcgi_script_name;</div><div class='line' id='LC48'>		include  fastcgi_params;</div><div class='line' id='LC49'>	}</div><div class='line' id='LC50'><br/></div><div class='line' id='LC51'>	# Settings | General | Blog address (URL)</div><div class='line' id='LC52'>	location / {</div><div class='line' id='LC53'>		# WordPress handles this perfectly as-is</div><div class='line' id='LC54'>		fastcgi_pass  fastcgi_cluster;</div><div class='line' id='LC55'>		# physical location on server</div><div class='line' id='LC56'>		fastcgi_param SCRIPT_FILENAME /PATH/TO/WP-DIR/index.php;</div><div class='line' id='LC57'>		include  fastcgi_params;</div><div class='line' id='LC58'>	}</div><div class='line' id='LC59'>}</div><div class='line' id='LC60'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167455/9a782be61b91ea7f7fd59223fee129d7a585c19b/nginx_vhost_example.conf" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167455#file_nginx_vhost_example.conf" style="float:right;margin-right:10px;color:#666">nginx_vhost_example.conf</a>
            <a href="https://gist.github.com/2167455">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>I&#8217;ll provide the content of <code>extra/proxy.conf</code> and <code>fastcgi_params</code> down below &#8212; they won&#8217;t surprise you &#8212; plus the configuration for my upstream <code>fastcgi_cluster</code>.</p>
<p>The purpose of <code>DOMAIN.NAME</code> and <code>LOGFILE</code> is obvious, so let&#8217;s skip to the useful stuff.</p>
<h4><code>/PATH/TO/WP-DIR</code></h4>
<p>This is simply the fully-qualified path to the directory where you have installed WordPress.  Big shock, I know.  I put mine in &#8216;/var/www/wordpress&#8217;.</p>
<h4><code>WP-CONTEXT</code></h4>
<p>Specifically, I&#8217;m referencing the <em>&#8216;WordPress address (URL)&#8217;</em> capture block.</p>
<p>WordPress 2.7 <a href="http://codex.wordpress.org/Giving_WordPress_Its_Own_Directory">supports a differentiation</a> between the root context of your blog and the root context of the WordPress resources themselves.  I&#8217;ve taken this approach &#8230; the URL of this blog post is root-relative to my virtual hostname, but if you do a View Source you&#8217;ll see:</p>
<div class="pre_wrap">
<pre><code>&lt;link rel="stylesheet" href="http://blog.cantremember.localhost/<strong>wordpress</strong>/wp-content/themes/cantremember/style.css" type="text/css" media="screen" /&gt;
...
&lt;link rel="pingback" href="http://blog.cantremember.localhost/<strong>wordpress</strong>/xmlrpc.php" /&gt;</code></pre>
</div>
<p>It&#8217;s a nice-to-have, and in many ways allows the configuration to be somewhat easier.  In WordPress Admin, under <em>Settings | General</em>, I have configured:</p>
<ul>
<li><em>WordPress address (URL)</em> = http://blog.cantremember.localhost/wordpress</li>
<li><em>Blog address (URL)</em> = http://blog.cantremember.localhost</li>
</ul>
<p>So my <code>WP-CONTEXT</code> is &#8216;wordpress&#8217;.  </p>
<h4>Usage</h4>
<p>Here are the core differentiations between my config and the Elastic Dog one:</p>
<p>My core two sections are the ones with <code>WP-CONTEXT</code>.  Before doing anything, I make the <code>$request_filename</code> context-less, so that it&#8217;s corrected relative to root.  Granted, I could have skipped that step because I used &#8216;wordpress&#8217; for each, but that doesn&#8217;t make for as good an example, and regex&#8217;s aren&#8217;t that expensive (don&#8217;t they have dedicated chips for them by now?).</p>
<p>I was having issues when WordPress wanted to take me to the Admin screen.  It used the shortcut &#8216;<code>/WP-CONTEXT/wp-admin</code>&#8216;, which is great if you&#8217;re not doing all this fancy re-writing and <code>fastcgi_index</code> can take over.  But we <em>are</em> being fancy.  That&#8217;s why the <code>$request_filename/index.php</code> text exists.  It works like a charm, although there may be a more efficient way to do this.</p>
<p>And here is where it became an advantage to differentiate between blog URLs and WordPress resources.  I&#8217;ve chosen to make my permalinks dateless &#8212; <code>/%postname%/</code> .  Call me crazy, but I like the way it looks on <a href="http://laughingsquid.com/bush-street-renamed-obama-street-in-san-francisco/">Laughing Squid</a>.  Given that&#8217;s the case, it&#8217;s hard to differentiate between &#8216;/some-permalink/&#8217; and &#8216;/wp-admin/&#8217;.  Splitting them off with the &#8216;wordpress&#8217; context made this possible.</p>
<p>The final context-less <em>&#8216;Blog address (URL)&#8217;</em> capture block is exactly what you&#8217;d expect.</p>
<h3>PmWiki</h3>
<p>I liked the simplicity and capabilities of <a href="http://www.pmwiki.org/">PmWiki</a> 2.2.0.  It&#8217;s an easier decision, since I have no intention of being a grand-scale collective document facility.  PmWiki is a powerful and flexible implementation with a lot of great processing directives that you can embed in a page.  Yet that also makes security something of a concern (as <a href="http://www.shlomifish.org/philosophy/computers/web/which-wiki/update-2006-07/#pmwiki">some reviewers</a> will point out as well).  Global multi-tier <a href="http://www.pmwiki.org/wiki/PmWiki/PasswordsAdmin">password auth</a> is available, and <a href="http://www.pmwiki.org/wiki/PmWiki/AuthUser">user-based auth</a> is available as necessary.</p>
<p>This configuration is a natural extension of the WordPress one above:</p>
<div id="gist-2167466" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>server {</div><div class='line' id='LC2'>	include  extra/proxy.conf;</div><div class='line' id='LC3'>	include  mime.types;</div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'>	listen  80;</div><div class='line' id='LC6'>	server_name  wiki.DOMAIN.NAME;</div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>	access_log  logs/LOGFILE-access.log;</div><div class='line' id='LC9'>	###error_log  logs/LOGFILE-error.log  debug;</div><div class='line' id='LC10'>	error_log  logs/LOGFILE-error.log;</div><div class='line' id='LC11'><br/></div><div class='line' id='LC12'><br/></div><div class='line' id='LC13'><br/></div><div class='line' id='LC14'>	# PHP execution only</div><div class='line' id='LC15'>	location ~ \.php$ {</div><div class='line' id='LC16'>		fastcgi_pass  fastcgi_cluster;</div><div class='line' id='LC17'>		fastcgi_index pmwiki.php;</div><div class='line' id='LC18'>		# physical location on server</div><div class='line' id='LC19'>		fastcgi_param SCRIPT_FILENAME /PATH/TO/PMWIKI-DIR$fastcgi_script_name;</div><div class='line' id='LC20'>		include  fastcgi_params;</div><div class='line' id='LC21'>	}</div><div class='line' id='LC22'><br/></div><div class='line' id='LC23'>	location / {</div><div class='line' id='LC24'>		# physical location on server</div><div class='line' id='LC25'>		root  /PATH/TO/PMWIKI-DIR;</div><div class='line' id='LC26'>		index  index.php index.html index.htm;</div><div class='line' id='LC27'><br/></div><div class='line' id='LC28'>		# this serves static files that exist without running other rewrite tests</div><div class='line' id='LC29'>		if (-f $request_filename) {</div><div class='line' id='LC30'>			expires 30d;</div><div class='line' id='LC31'>			break;</div><div class='line' id='LC32'>		}</div><div class='line' id='LC33'><br/></div><div class='line' id='LC34'>		# this sends all non-existing file or directory requests to index.php</div><div class='line' id='LC35'>		if (!-e $request_filename) {</div><div class='line' id='LC36'>			# ? =&amp;gt; &amp;amp;, then remove the leading /</div><div class='line' id='LC37'>			rewrite  ^(.*)\?(.*)$  $1&amp;amp;$2;</div><div class='line' id='LC38'>			rewrite  ^/(.+)$  /?n=$1  last;</div><div class='line' id='LC39'>		}</div><div class='line' id='LC40'>	}</div><div class='line' id='LC41'>}</div><div class='line' id='LC42'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167466/b6e2de443c35811bc4220e6e148cc8137cbc6837/nginx_wordpress_example.conf" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167466#file_nginx_wordpress_example.conf" style="float:right;margin-right:10px;color:#666">nginx_wordpress_example.conf</a>
            <a href="https://gist.github.com/2167466">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Everything here is obvious, including <code>/PATH/TO/PMWIKI-DIR</code>.  Mine is &#8216;/var/www/pmwiki&#8217;.  Here&#8217;s the lowdown:</p>
<p>In the <code>*.php</code> capture block, you&#8217;ll see that the default script is <code>pmwiki.php</code>.  I had created a symlink to rename it <code>index.php</code>, but after my config re-adjustment, that became obsolete.</p>
<p>The non-existing file test will be triggered by the following requests:</p>
<ul>
<li>/Main/HomePage</li>
<li>/Main/HomePage?action=edit</li>
</ul>
<p>Those URLs exist because I&#8217;m leveraging a feature called <a href="http://www.pmwiki.org/wiki/PmWiki/LayoutVariables#EnablePathInfo"><code>$EnablePathInfo</code></a>.  The referenced documentation doesn&#8217;t do it justice &#8230; this feature allows me to have bare <em>Group/Name</em> URLs, much like I&#8217;m doing with my bare blog URLs.  I&#8217;ll just say that I&#8217;m being SEO-minded and leave it at that.</p>
<p>Turning on that feature informs PmWiki to generate the URLs in that format, and it also makes the PHP script capable of parsing the CGI headers to do-the-right-thing.  My original configuration required me to perform the following override hack:</p>
<div class="pre_wrap">
<pre><code>include  fastcgi_params;
fastcgi_param SCRIPT_NAME '';</code></pre>
</div>
<p>But the revised configuration above simply re-writes the URL into the standard <code>'?n=</code>&#8216; format and the script never has to deal with CGI headers.  The only other rewrite considerations were to transform any querystring &#8216;?&#8217; into &#8216;&amp;&#8217; and to remove the leading &#8216;/&#8217; from the <em>Group/Name</em> combo.</p>
<p>Hooray!</p>
<h3>Supporting Configuration</h3>
<p>For all means and purposes, I&#8217;m using nginx&#8217;s default <code>fastcgi_params</code>.</p>
<p>This is <code>extra/proxy.conf</code>, derived from their <a href="http://wiki.codemongers.com/NginxFullExample">NginxFullExample</a>, with notes-to-self intact:</p>
<div id="gist-2167469" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'># can&#39;t set this here!</div><div class='line' id='LC2'>###proxy_redirect          default;</div><div class='line' id='LC3'><br/></div><div class='line' id='LC4'>proxy_set_header        Host            $host;</div><div class='line' id='LC5'>proxy_set_header        X-Real-IP       $remote_addr;</div><div class='line' id='LC6'>proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;</div><div class='line' id='LC7'><br/></div><div class='line' id='LC8'>#client_max_body_size    10m;</div><div class='line' id='LC9'>#client_body_buffer_size 128k;</div><div class='line' id='LC10'><br/></div><div class='line' id='LC11'>proxy_connect_timeout   15;</div><div class='line' id='LC12'>proxy_send_timeout      90;</div><div class='line' id='LC13'>proxy_read_timeout      30;</div><div class='line' id='LC14'><br/></div><div class='line' id='LC15'># turn off for Comet!</div><div class='line' id='LC16'>proxy_buffering         on;</div><div class='line' id='LC17'>proxy_buffers           32 8k;</div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'>proxy_ignore_client_abort  on;</div><div class='line' id='LC20'><br/></div><div class='line' id='LC21'># for caching, via Last-Modified:</div><div class='line' id='LC22'>#proxy_store          on;</div><div class='line' id='LC23'>#proxy_store_access   user:rw  group:rw  all:r;</div><div class='line' id='LC24'>#proxy_temp_path</div><div class='line' id='LC25'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167469/c6fa22d77376398dff399d6ee0381091377c7158/nginx_proxy_example.conf" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167469#file_nginx_proxy_example.conf" style="float:right;margin-right:10px;color:#666">nginx_proxy_example.conf</a>
            <a href="https://gist.github.com/2167469">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>This is <em>fastcgi_cluster</em>, which is just a simple example of how to do clustering:</p>
<div id="gist-2167472" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>upstream fastcgi_cluster {</div><div class='line' id='LC2'>	# sticky by IP</div><div class='line' id='LC3'>	###ip_hash;</div><div class='line' id='LC4'><br/></div><div class='line' id='LC5'>	# max_fails=3  fail_timeout=15s  weight=2</div><div class='line' id='LC6'>	# down  backup</div><div class='line' id='LC7'>	server  127.0.0.1:PORT-1;</div><div class='line' id='LC8'>	server  127.0.0.1:PORT-2;</div><div class='line' id='LC9'>	server  127.0.0.1:PORT-3;</div><div class='line' id='LC10'>}</div><div class='line' id='LC11'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167472/0141db36e37da5c2a3d02c61447f97ba7aba5585/nginx_upstream_cluster_example.conf" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167472#file_nginx_upstream_cluster_example.conf" style="float:right;margin-right:10px;color:#666">nginx_upstream_cluster_example.conf</a>
            <a href="https://gist.github.com/2167472">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>I tried using some of the additional server setting features &#8212; commented out above &#8212; but they weren&#8217;t working in my build of 0.7.27.  I can live without them at the moment, but the upstream block capabilities are quite powerful.</p>
<p>I&#8217;m running 3 FastCGI instances, each with 5 worker threads.  Again, good enough for government work.  I custom-built <code>fcgi</code> on OS X, but for my <a href="http://aws.amazon.com">AWS</a> Fedora Core 8 image I just went with <code>spawn-fcgi</code> that comes along with the <code><a href="http://www.lighttpd.net/">lighttpd</a></code> package.</p>
<p>This cluster config is also a nice starter reference for adding load-balancing capabilities to external AWS instances.  Given the volatile nature of VM image mappings, I&#8217;ve split the cluster config off into its own file for scripted generation.</p>
<h3>In Summary</h3>
<p>I&#8217;m very pleased with nginx.  It has been very stable &#8212; the only time I&#8217;ve taken it down is when setting it up for infinte <code>HTTP 302</code> redirects, and even then it took several hours of user activity to knock it over.  The configuration syntax is very powerful, and I haven&#8217;t once been wistful for my old <a href="http://httpd.apache.org/">Apache</a> habits :) .</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/wordpress-and-pmwiki-under-nginx/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Learning Ruby through Assertions and Podcasts</title>
		<link>http://blog.cantremember.com/learning-ruby-through-assertions-and-podcasts/</link>
		<comments>http://blog.cantremember.com/learning-ruby-through-assertions-and-podcasts/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 00:59:34 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Learning]]></category>
		<category><![CDATA[groovy]]></category>
		<category><![CDATA[podcast]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=50</guid>
		<description><![CDATA[I&#8217;ve been working with the Ruby language since March 2008. So (as of this writing) I&#8217;m still on the n00b path. Assertions The first thing I did was to follow the great advice of Dierk Koenig, writer of Groovy in Action and general Groovy / Grails advocate. The book itself doesn&#8217;t use the typical print-the-result-based [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working with the Ruby language since March 2008.  So (as of this writing) I&#8217;m still on the <a href="http://www.urbandictionary.com/define.php?term=n00b">n00b</a> path.</p>
<h3>Assertions</h3>
<p>The first thing I did was to follow the great advice of <a href="http://www.manning.com/koenig/">Dierk Koenig</a>, writer of <strong>Groovy in Action</strong> and general <a href="http://groovy.codehaus.org/">Groovy</a> / <a href="http://grails.org/">Grails</a> advocate.  The book itself doesn&#8217;t use the typical print-the-result-based code examples; it encourages the reader to learn the language <a href="http://groovy.codehaus.org/Quick+Start">through assertions</a>.  And that&#8217;s how I learned Groovy; I took the examples from the book, paraphrased them, tried variations on a theme, and then asserted that my results were true.  Now when I need to know how to use a language feature, I simply look back at my assertion code to re-edjumicate myself.</p>
<p>I learned the core Ruby language via <a href="http://rubyforge.org/projects/test-unit">Test::Unit</a>.  I spent three weeks (please don&#8217;t laugh) worth of my daily commute writing assertions for the core language, the standard packages, plus ActiveRecord and other common gems.  It allowed me to get a handle on the concepts, syntax, semantics and the sheer range of capabilities of he language.  I frequently look back at my <code>language_test_unit.rb</code> to figure out the best use of <code>Array.collect</code>, <code>catch..throw</code>, <code>Regexp</code> quirks, and using declared <code>lambda</code>s as blocks (etc).  More importantly, I&#8217;ve already written code using all of those techniques, so it&#8217;s just a referesher.</p>
<p>I <em>cannot recommend this technique enough</em> for coming up to speed on a language!</p>
<h3>Podcasts</h3>
<p>With that under my belt, plus some command-line scripts and a solid Rails project, I&#8217;m not spending time back-consuming posts from the following Podcast blogs:</p>
<h4>Rubyology</h4>
<p><a href="http://www.rubyology.com/">Site</a> : <a href="http://feeds.feedburner.com/rubyology">Feed</a></p>
<p>I&#8217;m actively back-consuming a lot of content from this wealth that Chris Matthieu has provided.  There are some great talks on <a href="http://www.rubyology.com/podcasts/show/70">Journeta</a>, using <a href="http://www.rubyology.com/podcasts/show/59">EC2</a>, great tutorials covering <a href="http://www.rubyology.com/podcasts/show/44">basic</a> and <a href="http://www.rubyology.com/podcasts/show/45"> RoR, and some <a href="http://www.rubyology.com/podcasts/show/13">scaling recommendations</a>.</p>
<h4>sd.rb Podcast</h4>
<p><a href="http://podcast.sdruby.com/">Site</a> : <a href="http://feeds.feedburner.com/sdrbpodcast">Feed</a></p>
<p>Straight from the mouth of the San Diego Ruby Users group.  A good variety of topics, focusing more on the Ruby language than on the Rails poster-child itself.  Nice talks on <a href="http://podcast.sdruby.com/2008/6/5/episode-046-rspec-in-15-minutes">rspec</a>, <a href="http://podcast.sdruby.com/2008/4/28/episode-041-mysql-clustering">MySQL clustering</a> and <a href="http://podcast.sdruby.com/2008/9/12/episode-053-ruby-arduino-development-rad">Arduino</a>, amongst many others.</p>
<h4>Railscasts</h4>
<p><a href="http://railscasts.com/">Site</a> : <a href="http://feeds.feedburner.com/railscasts">Feed</a></p>
<p>With 145 postings and counting, there&#8217;s a lot to be consumed here.  However, this is the last on my list, because none of them download to my iPhone 3G :( .  Lots of cross coverage on <a href="http://railscasts.com/episodes/133-capistrano-tasks">Capistrano</a>, <a href="http://railscasts.com/episodes/99-complex-partials">Partials</a>, custom <a href="http://railscasts.com/episodes/70-custom-routes">Routes</a>, <a href="http://railscasts.com/episodes/54-debugging-with-ruby-debug">ruby-debug</a> &#8230; the list goes on.</p>
<h4>Ruby on Rails Podcast</h4>
<p><a href="http://podcast.rubyonrails.org/">Site</a> : <a href="http://feeds.feedburner.com/rubyonrailspodcast">Feed</a></p>
<p>Geoffrey Grosenbach&#8217;s podcasts are seminal.  I&#8217;ll leave it up to the reader to <a href="http://answers.yahoo.com/question/index?qid=20080108230303AARcDfm">pore through</a> the years of accumulated wisdom.  How can you go wrong when you&#8217;re part of the <a href="http://rubyonrails.org">rubyonrails.org</a> domain!</p>
<h3>In Summary</h3>
<p>A number of these feeds provide screencasts and/or video.  A few of the files are old-school QuickTime MOVs which are problematic for the iPhone, which is annoying (definitely not the podcaster&#8217;s fault&#8230; get your head in gear, Apple).  And unfortunately when I break away to write down something in <a href="http://evernote.com/"><strong>Evernote</strong></a><strong>.app</strong>, and there&#8217;s any visuals associated with the cast, the iPhone halts playback.  <em>Grr.</em>  So I&#8217;m getting into the archaic habit of creating a <strong>Notes</strong> page and mailing it to myself :)</p>
<p>I recommend <em>each and all</em> of these podcasts.  Be prepared to sink a lot of time into them, so you might as well upload them onto your iPhone and take them to the beach!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/learning-ruby-through-assertions-and-podcasts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Remote Debugging using JConsole, JMX and SSH Tunnels</title>
		<link>http://blog.cantremember.com/debugging-with-jconsole-jmx-ssh-tunnels/</link>
		<comments>http://blog.cantremember.com/debugging-with-jconsole-jmx-ssh-tunnels/#comments</comments>
		<pubDate>Thu, 22 Jan 2009 22:35:10 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[jmx]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[tomcat]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=43</guid>
		<description><![CDATA[I&#8217;ve recently hosted my Spring / Hibernate webapp in the cloud thanks to Amazon Web Services. A future post will mention the monitoring I&#8217;ve put in place, but Tomcat keeps dying after about 36 hours. The first thing I need to do is enable debugging and JMX remoting so that I can put JConsole to [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently hosted my Spring / Hibernate webapp in the cloud thanks to <a href="http://aws.amazon.com/">Amazon Web Services</a>.  A future post will mention the monitoring I&#8217;ve put in place, but Tomcat keeps dying after about 36 hours.  The first thing I need to do is enable debugging and JMX remoting so that I can put JConsole to work.</p>
<p>This turned out not to be as easy as I&#8217;d liked.  Even with a bevy of useful resources available &#8212; lotsa people have run into these issues &#8212; it took a while to get the right combination.  Let&#8217;s hope I can save you a bit of that pain &#8230;</p>
<h3>JConsole / JMX Remoting via SSH Tunnels</h3>
<p>As I mentioned, I&#8217;m hosting this solution in the cloud.  So, when things go bad, I need to be able to debug remotely.  I don&#8217;t want to open up my security group to general traffic, so using SSH tunnels is the best option.  <a href="http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html">JConsole</a> is a great tool for measuring current statistics and performance of your Java app, and relies on JMX remoting.  It works great locally, or even within a trusted network, but once you&#8217;re <em>behind a firewall</em> with broad port-blocking, there are some significant issues.  There are several core Java forum topics related to this discussion:</p>
<ul>
<li><a href="http://forums.sun.com/thread.jspa?threadID=5267091">http://forums.sun.com/thread.jspa?threadID=5267091</a></li>
<li><a href="http://forum.java.sun.com/thread.jspa?forumID=4&amp;threadID=565139">http://forum.java.sun.com/thread.jspa?forumID=4&amp;threadID=565139</a></li>
<li><a href="http://forum.java.sun.com/thread.jspa?forumID=4&amp;threadID=5119655">http://forum.java.sun.com/thread.jspa?forumID=4&amp;threadID=5119655</a></li>
<li><a href="http://forum.java.sun.com/thread.jspa?forumID=58&amp;threadID=703567">http://forum.java.sun.com/thread.jspa?forumID=58&amp;threadID=703567</a></li>
</ul>
<p>Daniel Fuchs has <a href="http://blogs.sun.com/jmxetc/tags/firewall">written several articles</a> which illustrate these issues and provide good solutions.  <a href="http://blogs.sun.com/jmxetc/entry/connecting_through_firewall_using_jmx">He explains that</a> JMX remoting needs two ports:  one for the RMI registry, and one for the RMI connection objects which are stubs used for remoting all the critical data.  If you&#8217;re using the default JVM agent, you&#8217;ll tend to use the following JVM System.property&#8217;s on the server:</p>
<div class="pre_wrap">
<pre>-Djava.rmi.server.hostname
-Djava.rmi.server.useLocalHostname
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port
-Dcom.sun.management.jmxremote.ssl
-Dcom.sun.management.jmxremote.ssl.need.client.auth
-Dcom.sun.management.jmxremote.authenticate
-Dcom.sun.management.jmxremote.access.file
-Dcom.sun.management.jmxremote.password.file</pre>
</div>
<p>I&#8217;ll come back to these, but the one that&#8217;s important here is <code>jmxremote.port</code>.  It allows you to specify the RMI registry port, the one that you&#8217;ll use to establish your remote connection via JConsole.  However, the port for RMI export, which is used for all the critical data callbacks, is <em>randomly chosen</em> (on a session basis or JVM basis, not sure) and cannot be specified.  And you can&#8217;t open a port for tunneling if you don&#8217;t know what it is.</p>
<p>You can see this issue if you <a href="http://blogs.sun.com/jmxetc/entry/troubleshooting_connection_problems_in_jconsole">crank up the debugging</a> on JConsole.  I was having issues getting the logging output so I took the double-barrel approach, using both the <code>-debug</code> argument and the custom <code>java.util.logging</code> descriptor, the contents of which <a href="http://blogs.sun.com/roller/resources/jmxetc/logging.properties">I stole from here</a>.  Invoke it as follows:</p>
<div class="pre_wrap">
<pre><code>jconsole -debug -J"-Djava.util.logging.config.file=<strong>FILENAME</strong>"</code></pre>
</div>
<p>The quotes are optional.  Provide the logging descriptor filename.  You can call out the JMX Service URL or the hostname:port combination at the end if you like.  Now you&#8217;ll eventually a debug output much like this:</p>
<div class="pre_wrap">
<pre>FINER: [javax.management.remote.rmi.RMIConnector: jmxServiceURL=service:jmx:rmi:///jndi/rmi://localhost:<strong>PORT</strong>/jmxrmi] connecting...
FINER: [javax.management.remote.rmi.RMIConnector: jmxServiceURL=service:jmx:rmi:///jndi/rmi://localhost:PORT/jmxrmi] finding stub...
FINER: [javax.management.remote.rmi.RMIConnector: jmxServiceURL=service:jmx:rmi:///jndi/rmi://localhost:PORT/jmxrmi] connecting stub...
FINER: [javax.management.remote.rmi.RMIConnector: jmxServiceURL=service:jmx:rmi:///jndi/rmi://localhost:PORT/jmxrmi] getting connection...
FINER: [javax.management.remote.rmi.RMIConnector: jmxServiceURL=service:jmx:rmi:///jndi/rmi://localhost:PORT/jmxrmi] failed to connect: java.rmi.ConnectException: Connection refused to host: <strong>IP-ADDRESS</strong>; nested exception is:
java.net.ConnectException: Operation timed out</pre>
</div>
<p><code>PORT</code> will be the RMI registry port you&#8217;re tunneling into.  <code>IP-ADDRESS</code> is special, we&#8217;ll get to that, and it&#8217;s important to note that it&#8217;s a &#8216;<code>ConnectException</code>&#8216; occurring against that host.</p>
<p>This debugging information can show up rather late in the whole connection process, undoubtedly because it&#8217;s an &#8216;<code>Operation timed out</code>&#8216; issue, so don&#8217;t be surprised if it takes a while.  Fortuntely, you can also see immediate verbose feedback when you set up your ssh tunnel connection (see below).</p>
<h4>Addressing the Randomly-Generated RMI Export Port</h4>
<p>The first problem I chose to resolve was the one relating to the random RMI export port issue.  Daniel has <a href="http://blogs.sun.com/jmxetc/entry/connecting_through_firewall_using_jmx">provided a fine example</a> of how to implement a custom &#8216;pre-main&#8217; Agent which you can use to supplant the standard JVM one.  There&#8217;s his quick&#8217;n'dirty version which doesn&#8217;t address security &#8212; which is where I started.  And then there&#8217;s a <a href="http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html#gdfvq">more full-fledged version</a>, which I modified to be configurable.</p>
<p>Most importantly, it builds its JMXConnectorServer with the following service URL:</p>
<div class="pre_wrap">
<pre><code>service:jmx:rmi://<strong>HOSTNAME</strong>:<strong>RMI-EXPORT-PORT</strong>/jndi/rmi://<strong>HOSTNAME</strong>:<strong>RMI-REGISTRY-PORT</strong>/jmxrmi</code></pre>
</div>
<p>Traditionally, you&#8217;ll see this service URL from the client perspective, where <code>HOSTNAME:RMI-EXPORT-PORT</code> is not defined and you just have &#8216;<code>service:jmx:rmi:///jndi/rmi://...</code>&#8216;.  JConsole will build that sort of URL for you if you just provide <code>HOSTNAME:RMI-REGISTRY-PORT</code> (eg. hostname:port) when connecting.</p>
<p>By calling out the <code>RMI-EXPORT-PORT</code> in the agent&#8217;s service URL, you can affix it and tunnel to it.  You can use the same port as the RMI registry; this only requires you to open one port for tunneling.</p>
<p>On your client / for JConsole, the <code>HOSTNAME</code> will probably be localhost, where you&#8217;ve opened your tunnel like so:</p>
<div class="pre_wrap">
<pre><code>ssh -N -v -L<strong>9999</strong>:<strong>REMOTE-HOST</strong>:9999 REMOTE-HOST</code></pre>
</div>
<p><code>9999</code> is just an example port.  <code>REMOTE-HOST</code> is the host you&#8217;re tunneling to.  You can remove the <code>-v</code> argument, but it&#8217;s good to have that around so that you can see the network activity.  You can also use the <code>-l</code> argument to specify the login username on the remote host.  Note that you&#8217;re opening the same port locally as you&#8217;re hitting on the server, <em>with no offset</em>.  You&#8217;ll need to open the same port because your agent on the server is going to need to know what port to callback to itself on for RMI export, and that won&#8217;t work if you have an offset.  So you might as well use the same port for both the RMI registry and RMI export, and just keep that one port available locally.</p>
<p>On the server in your agent, the <code>HOSTNAME</code> part of the service URL can either be <code>InetAddress.getLocalHost().getHostName()</code>, or an IP address (127.0.0.1), or in my case &#8216;localhost&#8217; just worked fine.</p>
<p>The major reason to create the custom agent is to build the port-qualifying service URL.  As usual, <a href="http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html#gdfvq">the example code</a> takes a lot of shortcuts.  So, I built myself a more re-usable agent &#8212; influenced by the standard JVM agent&#8217;s System.property&#8217;s &#8212; which allowed me to configure the same sorts of things as mentioned above:</p>
<ul>
<li><strong>hostname</strong> : to be used as the HOSTNAME value above</li>
<li><strong>port.registry</strong> : to be used for RMI-REGISTRY-PORT</li>
<li><strong>port.export</strong> : to be used for RMI-EXPORT-PORT, defaulting to the same as port.registry if not provided</li>
<li><strong>ssl</strong> : true to enable SSL</li>
<li><strong>authenticate</strong> : true to enable credentials / authentication</li>
<li><strong>access.file</strong> : specifies the location of the user credentials file</li>
<li><strong>password.file</strong> : specifies the location of the user password file</li>
</ul>
<p>And so I was able to configure my agent service URL for localhost, using the same port for both RMI requirements, and using simple password-based auth.  I did not go down the SSL route, though <a href="http://blogs.sun.com/jmxetc/tags/firewall">many of the posts</a> from Daniel and others explain this as well.  Do that once you&#8217;ve tackled the core problem :)</p>
<p><a href="http://www.junlu.com/msg/394744.html">Another great post</a> relating to this issue mentions that Tomcat has a custom Listener for setting up a similar agent.  The example was:</p>
<div class="pre_wrap">
<pre><code>&lt;Listener className="org.apache.catalina.mbeans.JMXAdaptorLifecycleListener" namingPort="<strong>RMI-REGISTRY-PORT</strong>" port="<strong>RMI-EXPORT-PORT</strong>" host="<strong>localhost</strong>"/&gt;</code></pre>
</div>
<p>I didn&#8217;t look any deeper into this to see whether it supports SSL and/or basic authentication.  But it seems clear that this *not* a Java agent, because you have to set those up via System.property&#8217;s.  Here&#8217;s what I needed to add to Tomcat startup for my custom agent:</p>
<div class="pre_wrap">
<pre>-D___.hostname=<strong>localhost</strong>
-D___.port.registry=<strong>RMI-REGISTRY-AND-EXPORT-PORT</strong>
-D___.authenticate=true
-D___.password.file=$CATALINA_HOME/conf/jmxremote.password
-D___.access.file=$CATALINA_HOME/conf/jmxremote.access
-javaagent:<strong>PATH/TO/AGENT</strong>.jar</pre>
</div>
<p>I&#8217;ve omitted the namespace I used for my agent, and the formal name of the &#8216;pre-main&#8217;-compatible JAR file that I built using <a href="http://blogs.sun.com/jmxetc/entry/connecting_through_firewall_using_jmx">the instructions that Daniel provided</a>.  Tomcat won&#8217;t start up properly until the agent is happy; after that, then you&#8217;re golden.</p>
<p>So I got Tomcat running, started up an ssh tunnel, and invoked JConsole.  And still <em>no matter what I did</em>, I still got <code>ConnectException: Operation timed out</code>.  I tried to connect via JConsole in all the following ways:</p>
<div class="pre_wrap">
<pre><strong>HOSTNAME:PORT</strong>
service:jmx:rmi:///jndi/rmi://<strong>HOSTNAME:PORT</strong>/jmxrmi
service:jmx:rmi://<strong>HOSTNAME:PORT</strong>/jndi/rmi://<strong>HOSTNAME:PORT</strong>/jmxrmi</pre>
</div>
<p>All of these are valid URLs for connecting via JConsole.  For a while there I wasn&#8217;t sure whether you could use the same port for both the RMI registry and export, so I could see that the JConsole log was different when I called out the RMI export info explicitly in the service URL.  Still, it didn&#8217;t seem to help.</p>
<p>Then I started to realize that there were <em>two separate issues</em> going on, although they tended to blend together a lot in the posts I&#8217;d been reading.</p>
<h4>Addressing the RMI Export Hostname</h4>
<p>The short version is, even if you&#8217;ve set up your JMX service URL properly on the server &#8212; yes, even if you&#8217;ve set its <code>HOSTNAME</code> up to be &#8216;localhost&#8217; &#8212; you&#8217;ll <strong>still</strong> need to tell JMX remoting which hostname the RMI export objects should use for callbacks.  This requires you to provide the following System.property&#8217;s as well:</p>
<div class="pre_wrap">
<pre>-Djava.rmi.server.hostname=localhost
-Djava.rmi.server.useLocalHostname=true</pre>
</div>
<p>The <code>useLocalHostname</code> may not be relevant, but it doesn&#8217;t hurt.  All this time I&#8217;d thought that because I was configuring that information in the service URL that RMI would build the objects accordingly.  But I was wrong &#8230; <em>it doesn&#8217;t</em> &#8230; you need to call that out separately.</p>
<p>What was not apparent to me &#8212; until I started to see the same articles pop up when I revised my search criteria &#8212; was the <code>IP-ADDRESS</code> in this exception dump:</p>
<div class="pre_wrap">
<pre>FINER: [javax.management.remote.rmi.RMIConnector: jmxServiceURL=service:jmx:rmi:///jndi/rmi://localhost:<strong>PORT</strong>/jmxrmi] failed to connect: java.rmi.ConnectException: Connection refused to host: <strong>IP-ADDRESS</strong>; nested exception is:
java.net.ConnectException: Operation timed out</pre>
</div>
<p>It was the IP address of my <em>instance in the cloud</em>.  The callbacks were being made back to my VM, but they needed to be made to &#8216;localhost&#8217; so that they could go through the tunnel that I&#8217;d opened.  The &#8216;<code>Operation timed out</code>&#8216; was due to the port being closed, which is the whole reason you&#8217;re using ssh tunnels in the first place.  Once the RMI exported objects know to use &#8216;localhost&#8217;, that addresses the problem.  And magically, JConsole will connect and show you all the data in the world about your server.</p>
<p>So you must provide those System.property&#8217;s above regardless of what other configuration you&#8217;ve provided in your custom JMX agent.</p>
<h3>Additional Concerns</h3>
<p>There were a number of other red herrings that I followed for a while, but they were called out as being potential issues, so I kept note.</p>
<ul>
<li>
If your server is running Linux, there are <a href="http://java.sun.com/javase/6/docs/technotes/guides/management/faq.html#linux1">a couple of things</a> you&#8217;ll want to check, to make sure that your /etc/hosts is self-referencing correctly, and that you&#8217;re not filtering out packets.
</li>
<li>
You will have troubles stopping Tomcat when it has been started with your custom JMX agent; you&#8217;ll have to kill the process.  Apparently agents don&#8217;t release their thread-pools very nicely.  Daniel provides an example of an agent with a <a href="http://blogs.sun.com/jmxetc/entry/more_on_premain_and_jmx">thread-cleaning thread</a> &#8212; which still has some limitations, and raises the philosophical question &#8216;<a href="http://watchmenmovie.warnerbros.com/"><em>who cleans the thread-cleaner</em></a>&#8216;? He also provides an agent that <a href="http://">can be remotely stopped</a> &#8212; which is reasonably complex.  I&#8217;ll save that one for a rainy day.
</li>
<li>
If you want to use SSL in your authentication chain, read up on Daniel&#8217;s other postings, and use the following System.property&#8217;s on both the server <em>and</em> when starting JConsole:</p>
<div class="pre_wrap">
<pre>-Djavax.net.ssl.keyStore
-Djavax.net.ssl.keyStorePassword
-Djavax.net.ssl.trustStore
-Djavax.net.ssl.trustStorePassword</pre>
</div>
</li>
<li>
I have <a href="http://blog.cantremember.com/remote-scripting-for-aws/">built some Ruby scripts</a> which allow me to dynamically look up an AWS instance&#8217;s public DNS entry and then start up a <code>Net::SSH</code> process with port-forwarding tunnels.  This works fine for HTTP and even for remote JVM debugging, but it <em>did not work</em> for JMX remoting.  I&#8217;m not sure why, so you should stick with using <a href="http://www.manpagez.com/man/1/ssh/"><code>ssh</code></a> for setting up your tunnels.
</li>
<li>
I started out this exercise using <a href="http://www.wireshark.org/">Wireshark</a> for packet sniffing.  I&#8217;m using OS X, and I installed Wireshark &#8212; the successor to Ethereal &#8212; via <a href="http://www.macports.org/">MacPorts</a>.  It runs under X11, which you&#8217;ll need to install from either Apple&#8217;s site or your Optional Installs DVD.  I couldn&#8217;t get any Interfaces (ethernet card, etc.) to show up, until <a href="http://www.gofixit.com/?p=144">I learned that I should</a>:</p>
<div class="pre_wrap">
<pre><code>sudo wireshark</code></pre>
</div>
<p>The app will warn you that this is unsafe, but it works.  The <a href="http://blog.nominet.org.uk/tech/2008/04/30/wireshark-capture-under-mac-os-x/">Nominet team says</a> that you can address this issue by providing:</p>
<div class="pre_wrap">
<pre><code>sudo chmod go+r /dev/bpf*</code></pre>
</div>
<p>However that is volatile, and has to be done whenever your Mac starts up.  More config involved, so I took the easy path.
</li>
<li>
If you&#8217;re using a script to start and stop Tomcat, you&#8217;ll need to somehow separate out the System.property&#8217;s that should be used on startup, and omit them when invoking shutdown.  If you invoke shutdown with your debug and/or RMI ports specified, the shutdown will fail because those ports are already in use.</p>
<p>I&#8217;m using the newest standard Tomcat RPM available for Fedora Core 8 &#8212; <code>tomcat5-5.5.27</code> &#8212; and it&#8217;s uniquely nutty in terms of how it is deployed:</p>
<div class="pre_wrap">
<pre>/etc/init.d/tomcat5
/usr/bin/dtomcat5
/etc/tomcat5/*.conf</pre>
</div>
<p>That&#8217;s a very non-standard arrangement.  The init.d script <code>awk</code>s the *.conf files, and a whole array of other exciting things.  I still haven&#8217;t gotten it to properly do an init.d <code>restart</code> due to how it blends the <code>JAVA_OPTS</code> handling.  So that&#8217;s left as a case-specific exercise.
</li>
<li>
The whole reason I went down this path was to address a memory leak relating to Hibernate sessions <a href="http://sleepbotzz.wordpress.com/2008/02/03/eliminating-leaks-in-the-sleepbot-stack/">which I blogged about</a> a long time ago.  The fix required me to invoke Tomcat with the following System.property&#8217;s:</p>
<div class="pre_wrap">
<pre>-Djavax.rmi.CORBA.PortableRemoteObjectClass
-Djava.naming.factory.initial</pre>
</div>
<p>The <code>org.objectweb.carol</code> JAR, which these settings were targeted at, is part of my weapp, so it&#8217;s available in its own Classloader.  However, once I put the custom JMX agent in place, I got:</p>
<div class="pre_wrap">
<pre>FATAL ERROR in native method: processing of -javaagent failed
Exception in thread "main" java.lang.reflect.InvocationTargetException
Caused by: java.lang.ClassNotFoundException: org.objectweb.carol.jndi.spi.MultiOrbInitialContextFactory</pre>
</div>
<p>Attempting to create a symlink to the app-specific JAR in either <code>common/lib</code>, <code>common/endorsed</code> or <code>shared/lib</code> did not address the issue.  I had to hack the JAR into the <code>--classpath</code> in order to get Tomcat to start.  And yes, <em>hack</em> was the operative term (again).
</li>
</ul>
<h3>In Summary</h3>
<p>Frankly, all that discovery was enough for one day.  And yes, it took me that long to find all of the corner-cases i was dealing with.  I hope that if you find this article that it will make your path a bit easier.  I know I&#8217;ll be glad that I blogged about the details the next time I bump into the issue!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/debugging-with-jconsole-jmx-ssh-tunnels/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>logrotate Mac OS Launch Daemon with Legacy MacPort</title>
		<link>http://blog.cantremember.com/logrotate-mac-os-launch-daemon-with-legacy-macport/</link>
		<comments>http://blog.cantremember.com/logrotate-mac-os-launch-daemon-with-legacy-macport/#comments</comments>
		<pubDate>Wed, 21 Jan 2009 07:04:06 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[logrotate]]></category>
		<category><![CDATA[macports]]></category>
		<category><![CDATA[osx]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=37</guid>
		<description><![CDATA[Everbody loves cron. The classic basic scheduler, 80/20 flexibility, gets the job done. So, when I started with OS X, I went looking for cron. Yes, you can cron if you want to. Or maybe, as the Mac children recommend, you can create Launch Daemons. It&#8217;s simply a custom launchd.plist, an XML file to define [...]]]></description>
			<content:encoded><![CDATA[<p>Everbody loves <a href="http://www.manpagez.com/man/8/cron/">cron</a>.  The classic basic scheduler, 80/20 flexibility, gets the job done.  So, when I started with OS X, I went looking for cron.</p>
<p>Yes, you can <em>cron</em> if you want to.  Or maybe, as the Mac children recommend, you can create <strong>Launch Daemons</strong>.  It&#8217;s simply a custom <a href="http://developer.apple.com/DOCUMENTATION/DARWIN/Reference/ManPages/man5/launchd.plist.5.html">launchd.plist</a>, an XML file to define tasks in Apple&#8217;s terms.  Sweet, I can do that&#8230;</p>
<h3>The logrotated.plist Daemon</h3>
<p>From what I could tell, out of the box, Leopard doesn&#8217;t give you <a href="http://linux.die.net/man/8/logrotate">logrotate</a>.  So, here it is as a Launch Daemon:</p>
<div id="gist-2167479" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></div><div class='line' id='LC2'><span class="cp">&lt;!DOCTYPE plist PUBLIC &quot;-//Apple Computer//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;</span></div><div class='line' id='LC3'><span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">&quot;1.0&quot;</span><span class="nt">&gt;</span></div><div class='line' id='LC4'><span class="nt">&lt;dict&gt;</span></div><div class='line' id='LC5'>	<span class="nt">&lt;key&gt;</span>Disabled<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC6'>	<span class="nt">&lt;false/&gt;</span></div><div class='line' id='LC7'>	<span class="nt">&lt;key&gt;</span>Label<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC8'>	<span class="nt">&lt;string&gt;</span>logrotate<span class="nt">&lt;/string&gt;</span></div><div class='line' id='LC9'><br/></div><div class='line' id='LC10'>	<span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC11'>	<span class="nt">&lt;array&gt;</span></div><div class='line' id='LC12'>		<span class="nt">&lt;string&gt;</span>/opt/local/sbin/logrotate<span class="nt">&lt;/string&gt;</span></div><div class='line' id='LC13'>		<span class="nt">&lt;string&gt;</span>-f<span class="nt">&lt;/string&gt;</span></div><div class='line' id='LC14'>		<span class="nt">&lt;string&gt;</span>/etc/logrotate/*.conf<span class="nt">&lt;/string&gt;</span></div><div class='line' id='LC15'>	<span class="nt">&lt;/array&gt;</span></div><div class='line' id='LC16'>	<span class="nt">&lt;key&gt;</span>EnableGlobbing<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC17'>	<span class="nt">&lt;true/&gt;</span></div><div class='line' id='LC18'><br/></div><div class='line' id='LC19'>	<span class="nt">&lt;key&gt;</span>KeepAlive<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC20'>	<span class="nt">&lt;false/&gt;</span></div><div class='line' id='LC21'>	<span class="nt">&lt;key&gt;</span>LaunchOnlyOnce<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC22'>	<span class="nt">&lt;false/&gt;</span></div><div class='line' id='LC23'>	<span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC24'>	<span class="nt">&lt;false/&gt;</span></div><div class='line' id='LC25'><span class="c">&lt;!--</span></div><div class='line' id='LC26'><span class="c">causes &#39;Throttling respawn: Will start in 10 seconds&#39; in system.log</span></div><div class='line' id='LC27'><span class="c">	&lt;key&gt;OnDemand&lt;/key&gt;</span></div><div class='line' id='LC28'><span class="c">	&lt;false/&gt;</span></div><div class='line' id='LC29'><span class="c">--&gt;</span></div><div class='line' id='LC30'><br/></div><div class='line' id='LC31'><span class="c">&lt;!--</span></div><div class='line' id='LC32'><span class="c">http://www.nabble.com/launchd-support-ranges-for-StartCalendarInterval - - td14857719.html</span></div><div class='line' id='LC33'><span class="c">--&gt;</span></div><div class='line' id='LC34'>	<span class="nt">&lt;key&gt;</span>StartCalendarInterval<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC35'>	<span class="c">&lt;!-- 3:05a, 3:05p --&gt;</span></div><div class='line' id='LC36'>	<span class="nt">&lt;array&gt;</span></div><div class='line' id='LC37'>		<span class="nt">&lt;dict&gt;</span></div><div class='line' id='LC38'>			<span class="nt">&lt;key&gt;</span>Hour<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC39'>			<span class="nt">&lt;integer&gt;</span>3<span class="nt">&lt;/integer&gt;</span></div><div class='line' id='LC40'>			<span class="nt">&lt;key&gt;</span>Minute<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC41'>			<span class="nt">&lt;integer&gt;</span>05<span class="nt">&lt;/integer&gt;</span></div><div class='line' id='LC42'>		<span class="nt">&lt;/dict&gt;</span></div><div class='line' id='LC43'>		<span class="nt">&lt;dict&gt;</span></div><div class='line' id='LC44'>			<span class="nt">&lt;key&gt;</span>Hour<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC45'>			<span class="nt">&lt;integer&gt;</span>15<span class="nt">&lt;/integer&gt;</span></div><div class='line' id='LC46'>			<span class="nt">&lt;key&gt;</span>Minute<span class="nt">&lt;/key&gt;</span></div><div class='line' id='LC47'>			<span class="nt">&lt;integer&gt;</span>05<span class="nt">&lt;/integer&gt;</span></div><div class='line' id='LC48'>		<span class="nt">&lt;/dict&gt;</span></div><div class='line' id='LC49'>	<span class="nt">&lt;/array&gt;</span></div><div class='line' id='LC50'><span class="nt">&lt;/dict&gt;</span></div><div class='line' id='LC51'><span class="nt">&lt;/plist&gt;</span></div><div class='line' id='LC52'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/2167479/3bdc691b8eaf5b053d50b481eea76104bec7de20/logrotate_launchdaemon.plist" style="float:right;">view raw</a>
            <a href="https://gist.github.com/2167479#file_logrotate_launchdaemon.plist" style="float:right;margin-right:10px;color:#666">logrotate_launchdaemon.plist</a>
            <a href="https://gist.github.com/2167479">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>Great.  So what do I do with it?  Name it anything &#8216;*.plist&#8217; &#8212; <code>logrotated.plist</code> perhaps &#8212; and put it here:</p>
<ul>
<li><code>/System/Library/LaunchDaemons</code> or</li>
<li><code>/Library/LaunchDaemons</code></li>
</ul>
<p>I tend to use System for any services I wan to launch on boot (<a href="http://linux.die.net/man/8/chkconfig">chkconfig</a>-ish), and anything that&#8217;s just a scheduled task is more pedestrian.</p>
<p>You register daemons through <a href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/launchctl.1.html">launchctl</a>.  When you change it, just:</p>
<div class="pre_wrap">
<pre><code>$ launchctl unload FILE.plist
$ launchctl load FILE.plist</code></pre>
</div>
<p>Sure, not a hard thing.  What I <em>did</em> want to point out were some of the things that I learned:</p>
<ul>
<li>
Provide each space-separated component of the command line as &lt;string /&gt; element in the ProgramArgument &lt;array /&gt;.  It&#8217;s the easiest way to go.
</li>
<li>
I tend to keep the <code>KeepAlive</code>, <code>LaunchOnlyOnce</code> and <code>RunAtLoad</code> all in sync for each daemon.  It seems to guarantee compatability.
</li>
<li>
I still haven&#8217;t gotten quite the hand of <code>OnDemand</code> (c&#8217;mon, I&#8217;m new to this).  When you are developing your daemons, keep a watch on <code>/var/log/system.log</code> &#8212; if you screw up, you&#8217;ll probably see it there in one fashion or another.
</li>
<li>
<code>StartCalendarInterval</code> is the equivalent of the cron schedule mask.  See the <a href="http://www.nabble.com/launchd-support-ranges-for-StartCalendarInterval">Nabble</a> post i commented above.  You can&#8217;t do ranges, but wildcards are do-able sorta by omission.  If you want mulltiple disparate schedules, as you see above, you need to pass in an &lt;array /&gt; of &lt;dict /&gt;s, not just a single &lt;dict /&gt; when you only need one mask.
</li>
<li>
Yes, datatypes matter!  I couldn&#8217;t figure out for the life of me why my daemon wouldn&#8217;t start, until I realized that my <code>Hour</code> and <code>Minute</code> values were configured as &lt;string /&gt;s &#8212; vs &lt;integer /&gt;s &#8212; because I&#8217;d been, well, you know, trying to do ranges and wildcards the cron-style way.  <code>launchctl</code> didn&#8217;t complain, it just didn&#8217;t &#8230; work.  So don&#8217;t do that.
</li>
<li>
Any daemon which has a schedule that fires off when the system is down will be automatically executed shortly after the system re-awakens.  I don&#8217;t know exactly how quickly, but I&#8217;ve seen it in action (though I&#8217;ve read some dispute about its efficacy in my searches).
</li>
</ul>
<h3>The Trials and Tribulations of MacPorts</h3>
<p>But of course this Epistle wouldn&#8217;t be any fun without a twist, right?  Well, lucky us, because it wasn&#8217;t <em>that easy</em>.</p>
<p>I&#8217;m using <a href="http://guide.macports.org/">MacPorts</a> as my <code>yum</code>-my package manager.  It&#8217;s great, and easy to set up, but it&#8217;s quite as stable as I&#8217;ve seen under Linux.  Or rather, I&#8217;ve seen a disproportionate number of issues in the times I&#8217;ve used it.  However, I&#8217;m eternally grateful because it saves me so much time &#8230; the successes far outweigh the problems.</p>
<p>When things do go bad, as they did when I built logrotate 3.7.7, you have to set up a custom Portfile and source repository so that you can effectively drive MacPorts to fetch and build your specific version.  logrotate 3.7.1_1 turned out to be much more stable.</p>
<p><a href="http://homepage.mac.com/simx/technonova/tips/using_experimental_macports_portfiles.html">Techonova</a> and <a href="http://journal.bitshaker.com/articles/2007/10/20/install-old-versions-of-ports-using-macports/">Joe Homs</a> go into very excellent and welcome detail in their posts on how to pull this off.  A summary of what you need to do is:</p>
<ul>
<li>
watch the build failure &#8212; debug with <code>port -d</code> &#8212; to identify where the dying source is located.  That&#8217;ll be PATH/TO/PROJECT
</li>
<li>
visit <code>http://trac.macosforge.org/projects<wbr>/macports/log/trunk/dports<wbr>/PATH/TO/PROJECT</code> and snag the Portfile.  That&#8217;s a MacPorts spec file, and you can tweak it to do your bidding.
</li>
<li>
create a source directory that you&#8217;ll keep around for a while (mine is <code>/Users/Shared/macports</code>).
</li>
<li>
register that directory &#8212; your local Portfile repository &#8212; with <code>/opt/local/etc/macports/sources.conf</code> (using <code>file://</code> protocol)
</li>
<li>
copy the Portfile into a PATH/TO/PROJECT sub-directory structure (eg. <code>sysutils/logrotate</code>).  Basically, the same path you&#8217;ll have snagged it from.
</li>
<li>
pull down a previous source revision and snapshot it locally.  That could come from SVN or git, tarball, whatever.  Start search from the <code>homepage</code> setting of the Portfile.
</li>
<li>
tweak the Portfile to &#8216;make sense&#8217; for the source you&#8217;ve pulled down.  If you based upon a close-enough version, it should be as easy as tweaking the version, etc. &#8212; no custom build tasks.  I&#8217;m glossing over when I say that it&#8217;s usually a matter of version-based naming conventions and MD5 checksums.
</li>
<li>
re-build the repository&#8217;s PortIndex (which you&#8217;ll need to do every time you make an addition / change)
</li>
</ul>
<p>Please feel free to consult the other two posts to fill in the details that I was so cavalier about.</p>
<h3>In Summary</h3>
<p>This was another one of those occasions when I was glad I kept track of what I was doing while I was in the moment. Pack your config files with comments, because they&#8217;re invaluable.  And drop READMEs around where you&#8217;re likely to find them &#8230; I filled in a lot of the extra details for this post from those breadcrumbs I left for myself, things I could have easily wasted 15-20m on re-discovering by braille :)</p>
<p>Peace.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/logrotate-mac-os-launch-daemon-with-legacy-macport/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Custom Ruby and RadRails on Mac OS X</title>
		<link>http://blog.cantremember.com/custom-ruby-and-radrails-on-mac-os-x/</link>
		<comments>http://blog.cantremember.com/custom-ruby-and-radrails-on-mac-os-x/#comments</comments>
		<pubDate>Wed, 21 Jan 2009 04:53:37 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[aptana]]></category>
		<category><![CDATA[eclipse]]></category>
		<category><![CDATA[gems]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=29</guid>
		<description><![CDATA[Let&#8217;s break this down, shall we &#8230; Building Ruby and RubyGems Many thanks to Dan Benjamin for his excellent posting on doing a custom build of Ruby &#38; RubyGems on OS X. I was just getting started with using a Mac at the time, and it was a very straight-forward and helpful guide. For most [...]]]></description>
			<content:encoded><![CDATA[<p>Let&#8217;s break this down, shall we &#8230;</p>
<h3>Building Ruby and RubyGems</h3>
<p>Many thanks to <a href="http://danbenjamin.com/articles/2008/02/ruby-rails-leopard">Dan Benjamin</a> for his <strong>excellent</strong> posting on doing a custom build of Ruby &amp; RubyGems on OS X.  I was just getting started with using a Mac at the time, and it was a very straight-forward and helpful guide.  For most software, I&#8217;ll use <a href="http://www.macports.org/">MacPorts</a> or standard Apple packages, but for Ruby I&#8217;ll make an exception.</p>
<p>For starters, I just want to expand a bit on his excellent tutorial.  I won&#8217;t re-iterate his whole piece, but the outline is:</p>
<ul>
<li>start with OS X Leopard, and XCode (from your Optional Installs DVD)</li>
<li>get /usr/local and its derivatives into your path</li>
<li>set aside a good long-term home for your custom build code</li>
<li>pull down the source for <a href="http://ruby-lang.org">Ruby</a></li>
<li>run ./configure, then build and install with make</li>
<li>pull down the source for <a href="http://rubyforge.org/projects/rubygems/">RubyGems</a>, run its setup.rb</li>
</ul>
<p>After that you can install all of the <a href="http://rubygems.org">gems</a> that your heart desires.  I initially pulled down source for the Ruby 1.9 candidate release, but there are significant enough language changes that it destabilized a lot of tried-and-true gems.  Instead, I went with the latest generation of 1.8.7.</p>
<p>I did however run into a little issue when installing the native mysql gem.  Dan&#8217;s instructions weren&#8217;t valid for my case.  Fortunately, the README.html in the gem suggested building it manually as such:</p>
<div class="pre_wrap">
<pre><code>$ sudo gem install mysql -- --with-mysql-config
  <em>or</em>
$ ruby extconf.rb --with-mysql-config</code></pre>
</div>
<p>That worked well, since I&#8217;d already installed MySQL and my <code>/etc/mysql.cnf</code> was in its standard location.  Hooray!</p>
<h3>Configuring RadRails to use the Custom-Built Componentry</h3>
<p>Since I switch back and forth between Ruby and Java, I chose to stick with the Eclipse IDE.  <a href="http://aptana.com/">Aptana</a> has created a nice development suite called <a href="http://aptana.com/rails">RadRails</a> which is highly componentized, and it works rather well for script development as well.  Yes, if I don&#8217;t use <a href="http://macromates.com/">TextMate</a> I&#8217;m a heretic, but at some point I&#8217;ll hunker down and pony out the bucks for it :)</p>
<p>The install of Eclipse 3.4 Ganymede that I started out with contained several plugins / features from <code>rubypeople.org</code>.  As well intended as they were, I ran into several areas of conflict between them and RadRails.  I thus chose to remove these plugins from Eclipse, and stability ensued.</p>
<p>However, even after removed, I still have two Eclipse sections for &#8216;Ruby&#8217;.  The one for RadRails is the one with fewer sub-sections (also known as &#8216;the one that looks <em>less</em> like the Java language configuration section&#8217;).</p>
<p>Since you&#8217;ve custom built your install of Ruby, you&#8217;ll want to make the following configuration changes to Eclipse / RadRails:</p>
<ul>
<li>Under <strong>Ruby | Interpreters</strong>, add a new Generic Ruby interpreter.  Its executable should be <code>/usr/bin/local/ruby</code>, and the rest will take care of itself (I didn&#8217;t specify any arguments).</li>
<li>Under <strong>Rails</strong>, set up the &#8216;rails&#8217; and &#8216;mongrel_rails&#8217; paths to reference the respective binaries in <code>/usr/local/bin</code>, since that&#8217;s where all of your installed gems will have been exposed.</li>
<li>Under <strong>Java | Build Path | Classpath Variables</strong>, you&#8217;ll want to add <strong>GEM_LIB</strong>, which should reference <code>/usr/local/lib/ruby/gems/1.8/gems</code>.  It seems out of place under &#8216;Java&#8217;, but the IDE was crying out for it loudly.</li>
</ul>
<p>That should address the majority of the IDE configuration.  If anything doesn&#8217;t quite work correctly, you may want to examine the steps that I describe below as being &#8216;optional&#8217;, particularly the one where I created a mock <code>Ruby.framework</code> setup.</p>
<h3>Optional (?) Configuration</h3>
<p>I question whether they&#8217;re mandatory or not because this configuration has been stable on my machine for a little while now, and I have a tendency to get very detail-oriented, not all of which turns out to actually be <em>necessary</em>.  In the spirit of completeness &#8212; yep, there&#8217;s detail-oriented for you right there! &#8212; I&#8217;ll lay out what I have put into play.</p>
<p>I&#8217;ve injected &#8216;/usr/local/bin&#8217; and &#8216;/usr/local/sbin&#8217; into <code>/etc/paths</code>; that works like a charm.</p>
<p>I consistently install gems as root, or else I end up with account-specific discrepancies driven by each <code>~/.gems</code> folder.  I merely point that out to illustrate that such user-specific directories exists :)</p>
<p>I&#8217;ve added the following <i>unnecessary</i> env var:</p>
<div class="pre_wrap">
<pre><code>export GEM_LIB=/usr/local/lib/ruby/gems/1.8/gems</code></pre>
</div>
<p>It matches the Classpath Variable you set in Eclipse (above).  If nothing else, it&#8217;s an easy &#8216;$&#8217; substitution when I need to go spelunking into gem source code.</p>
<p>There&#8217;s been at least once where I have also needed the following env var.  It should be configured to point at the expanded source code of your custom Ruby build.</p>
<div class="pre_wrap">
<pre><code>export RUBY_SOURCE_DIR=...</code></pre>
</div>
<p>I&#8217;ve created the following directory structure:</p>
<div class="pre_wrap">
<pre><code>$ mkdir -p /System/Library/Frameworks/Ruby.framework/Versions/1.8-Current</code></pre>
</div>
<p>Therein I have created a &#8216;usr&#8217; directory structure that matches the one you&#8217;ll find in <code>/System/Library/Frameworks<wbr>/Ruby.framework/Versions/1.8/usr</code> .  It&#8217;s all just symlinks into the appropriate <code>/usr/local</code> hierarchy.  I then co-opted the Current symlink:</p>
<div class="pre_wrap">
<pre><code>$ cd /System/Library/Frameworks/Ruby.framework/Versions
$ ls -l
1.8
1.8-Current
Current -&gt; 1.8
$ rm Current
$ ln -s 1.8-Current Current</code></pre>
</div>
<p>Consider that you may need to mimic certain aspects of the default Ruby framework configuration, which is the inverse of the mock framework setup that I just described.  Specifically, <code>/usr/local/lib/ruby</code> takes after <code>/System/Library/Frameworks<wbr>/Ruby.framework/Versions/Current/usr/lib/ruby</code>.</p>
<p>This is why I ended up with both a &#8216;/usr/local/lib/ruby/site_ruby&#8217; and &#8216;/usr/local/lib/ruby/vendor_ruby&#8217; subdir on my machine.</p>
<p>The standard location for the <code>ruby</code> executable is often used in a she-bang line for <code>bash</code> scripts.  The standard location is:</p>
<div class="pre_wrap">
<pre><code>#!/usr/bin/ruby</code></pre>
</div>
<p>Which of course was still pointing at the default OS X Ruby framework.  I renamed the existing symlink and replaced it with one to <code>/usr/local/bin/ruby</code>.  I did not do this for <code>gem, irb, rdoc</code> (etc.) since I don&#8217;t have she-bang needs for them.</p>
<h3>In Summary</h3>
<p>I hope some of this information is useful to you.  I was sitting at <a href="http://www.cafeinternational.com/">Cafe International</a> and setting up RadRails when the HD on my two-week-old MacBook Pro 2008 went into beachball-of-death mode.  I had to pick back up from there 2+ weeks later on a fresh laptop, which at least allowed me to keep these copious notes.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/custom-ruby-and-radrails-on-mac-os-x/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Getting Java SE 6 and Eclipse to play nicely on Mac OS X</title>
		<link>http://blog.cantremember.com/java-6-eclipse-os-x-leopard/</link>
		<comments>http://blog.cantremember.com/java-6-eclipse-os-x-leopard/#comments</comments>
		<pubDate>Tue, 20 Jan 2009 23:44:16 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[eclipse]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[osx]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=19</guid>
		<description><![CDATA[Specifically, this post is about getting the standard pre-packaged install of Java SE 6 for OS X Leopard to play nicely with Eclipse 3.4 Ganymede. If you wish to move to Java SE 6 for Tiger or a pre-10.5 release of OS X, you may want to consider using SoyLatte, as recommended by the 2 [...]]]></description>
			<content:encoded><![CDATA[<p>Specifically, this post is about getting the standard pre-packaged install of Java SE 6 for OS X Leopard to play nicely with Eclipse 3.4 Ganymede.  If you wish to move to Java SE 6 for Tiger or a pre-10.5 release of OS X, you may want to consider using <a href="http://landonf.bikemonkey.org/static/soylatte/">SoyLatte</a>, as <a href="http://2tbsp.com/node/106">recommended by the 2 tablespoons blog</a>. I don&#8217;t imagine that any 3.x version of Eclipse would raise additional issues.</p>
<p>When you unpack the installer .dmg, it&#8217;s simply going to install the package.  It will not automatically assume that you want to use it (not such a bad assumption, as it turns out).  So there are a few caveats you need to deal with.  I&#8217;m taking the approach of addressing all the details I know of, regardless of how obvious this may all seem :)</p>
<p>Post-install, Java 5 will still be in active use:</p>
<div class="pre_wrap">
<pre><code>$ ls -l /usr/bin/java
/usr/bin/java -&gt; /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
$ Lunditer:~ root# java -version
java version "1.5.0_16"</code></pre>
</div>
<p>You&#8217;ll see something much like this under <code>/System/Library/Frameworks<wbr>/JavaVM.framework/Versions</code> :</p>
<div class="pre_wrap">
<pre><code>1.3 -&gt; 1.3.1
1.3.1
1.4 -&gt; 1.4.2
1.4.1 -&gt; 1.4
1.4.2
1.5 -&gt; 1.5.0
1.5.0
1.6.0
A
Current -&gt; A
CurrentJDK -&gt; 1.5</code></pre>
</div>
<p>Initially, I went and modified the symlink for CurrentJDK.  Modifying the symlink for Current brought me nothing but <em>pain</em>. You can get the results you want by making the following change:</p>
<div class="pre_wrap">
<pre><code>CurrentJDK -&gt; 1.6.0</code></pre>
</div>
<p>I&#8217;d had it set up that way until I wrote this post.  But Apple <a href="http://www.macosxhints.com/article.php?story=2005111606295344">highly discourages this practice</a>.  Instead, they recommend that you use the Java Preferences pane.  Get rid of all your symlink shenanigans, and simply drag the &#8216;Java SE 6&#8242; option to the top of the applet and application listings.  It works like a charm.  Start a new Terminal shell and you can confirm:</p>
<div class="pre_wrap">
<pre><code>$ /usr/bin/java -version
java version "1.6.0_07"
$ /usr/bin/javac -version
javac 1.6.0_07</code></pre>
</div>
<p>However &#8230; from my experience, this is a <em>user-specific</em> setting.  Which means if you run Java as <code>root</code>, you&#8217;ll still get Java SE 1.5.  Not a problem.  We&#8217;ll stick with the Apple-sanctioned solution here.</p>
<p>Hooray, Java SE 6!  Now let&#8217;s start Eclipse 3.4!  Hooray &#8230; oops?  Startup failure dialog:</p>
<div class="pre_wrap">
<pre><code>JVM terminated.
Exit code=-1
...
-vm /System/Library/Frameworks/JavaVM.framework
...</code></pre>
</div>
<p>This is a <strong>well documented</strong> issue.  Both the <a href="http://blog.kischuk.com/2008/05/08/running-eclipse-on-macbooks-with-java-6/">Rob Kischuk</a> and <a href="http://stackoverflow.com/questions/245803/jvm-terminates-when-launching-eclipse-mat-on-mac-os-with-j2se-60">Stack Overflow</a> blogs are very helpful in explaining that; Eclipse uses 32-bit SWT-Cocoa / Carbon, and Mac OS Java SE 6 only comes in a 64-bit flavor.  I have not looked into the SoyLatte option, as suggested above, though that may prove to be an alternative approach.</p>
<p>I had some struggles getting their approach to work (until now, that is).  Easiest way to start config testing is to run Eclipse from the command line:</p>
<div class="pre_wrap">
<pre><code>$ cd /Applications/eclipse/Eclipse.app/Contents/MacOS/
$ ./eclipse -vm /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Commands/java</code></pre>
</div>
<p>You can also provide <code>/System/Library/Frameworks<wbr>/JavaVM.framework/Versions/1.5.0</code>, which I extrapolated from the -vm setting in the failure message.  If you still get an error dialog, you may see multiple instances of -vm, but that&#8217;s a red herring; it will work once properly configured.</p>
<p>Great.  Once that&#8217;s working for you, let&#8217;s apply it to the App shortcut.  There&#8217;s two ways you can go about this:</p>
<p>The blogs above recommend that you modify the <code>Info.plist</code>:</p>
<ul>
<li>edit /Applications/eclipse/Eclipse.app/Contents/Info.plist</li>
<li>uncomment the following line:
<div class="pre_wrap">
<pre><code>&lt;string&gt;-vm&lt;/string&gt;&lt;string&gt;/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Commands/java&lt;/string&gt;</code></pre>
</div>
</li>
</ul>
<p>Alternately, you can modify <code>eclipse.ini</code>, where you might also configure your JVM memory configuration:</p>
<ul>
<li>edit /Applications/eclipse/Eclipse.app/Contents/MacOS/eclipse.ini</li>
<li>add the following line:
<div class="pre_wrap">
<pre><code>-vm /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Commands/java</code></pre>
</div>
</li>
</ul>
<p>Either dab will do ya.  But if you didn&#8217;t know before, now you know where <code>eclipse.ini</code> lives :)</p>
<p>Things should be grand now.  If they aren&#8217;t, I suggest trying some variations while running Eclipse from the command line.  It took me a while to get the right combination, even though this post and (the ones it references) may seem very cookie-cutter.  It <em>will</em> eventually work for you!</p>
<p>Now, I mentioned that the Apple-sanctioned approach seems to be user-specific.  And I run Tomcat as root.  So you will probably still want to add the traditional environment settings.</p>
<p>You can either make these changes to <code>/etc/profile</code>, or to the <code>~/.bashrc</code> of each relevant account.  No surprises here:</p>
<div class="pre_wrap">
<pre><code>export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home
PATH="$JAVA_HOME/bin:$PATH"</code></pre>
</div>
<p>My experiments to get <code>/etc/paths</code> to inject something into the path before &#8216;/usr/bin&#8217; weren&#8217;t so useful, so I chose the brute force method.</p>
<p>If you&#8217;re going to use sudo to run Tomcat, you&#8217;ll want to make sure to load the bash environment.</p>
<div class="pre_wrap">
<pre><code>sudo su -</code></pre>
</div>
<p>to guarantee that root&#8217;s environment includes the custom /etc/profile overrides.  No dash, and it no work.  Tomcat Java 5 + App Java 6 =</p>
<div class="pre_wrap">
<pre><code>SEVERE: Error deploying web application directory ...
java.lang.UnsupportedClassVersionError: Bad version number in .class file</code></pre>
</div>
<p>With the environment in place, you should be good to go.</p>
<p>The writing of this post drove me to (a) drop the symlinks, (b) use Java Preferences pane and (c) get the right -vm config set up (since I&#8217;d been leaning entirely on the environment settings up to this point).  So there are definitely a few ways to skin this cat.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/java-6-eclipse-os-x-leopard/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

