<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" version="2.0">

<channel>
	<title>Ramblings</title>
	
	<link>http://ramblings.gibberishcode.net</link>
	<description>about fetching, interpreting, and executing.</description>
	<lastBuildDate>Wed, 11 Nov 2009 01:55:33 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/gibberishcode/vDzA" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
		<title>Implementing Ruby jobs in the background</title>
		<link>http://ramblings.gibberishcode.net/archives/implementing-ruby-jobs-in-the-background/38</link>
		<comments>http://ramblings.gibberishcode.net/archives/implementing-ruby-jobs-in-the-background/38#comments</comments>
		<pubDate>Fri, 06 Nov 2009 04:40:15 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby Language]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=38</guid>
		<description><![CDATA[I needed a way to kick off a background job that was triggered by an end-user on my Ramaze-backed website and could run for almost two hours!  This article brings together all the elements to get the job done.
Preamble (a.k.a. Ramblings)
I can hear you exclaiming, &#8220;Two hours!?  Are you nuts?  You really [...]]]></description>
			<content:encoded><![CDATA[<p>I needed a way to kick off a background job that was triggered by an end-user on my Ramaze-backed website and could run for almost two hours!  This article brings together all the elements to get the job done.</p>
<h2>Preamble (a.k.a. Ramblings)</h2>
<p>I can hear you exclaiming, &#8220;Two hours!?  Are you nuts?  You really don&#8217;t want to do that on a web application,&#8221; and you would be correct; I did not.  But alas, my users wanted it and they waved money in front of my face to get it done.  What&#8217;s a poor sap like me to do but to comply?</p>
<p>So, what is this crazy task that needs to run so urgently it couldn&#8217;t wait until a midnight cronjob task to kick it off?  Well, the task is basically exporting data from the live production database to a reporting database and the users sometimes have to generate those reports as soon as humanly possible against the then current production data.  The problem is, the export process takes anywhere from 15 minutes to almost two hours, depending on how much data has changed since the last midnight run.</p>
<p>Such a long-running process is clearly outside the normal bounds of a website&#8217;s usual request/respond lifecycle and, if you&#8217;re like me and fronting your web app with Phusion&#8217;s Passenger, you&#8217;re quite at risk of Passenger&#8217;s process recycling action killing the task in mid-stream and losing your work in progress.</p>
<p>I didn&#8217;t quite know what I was going after when I started solving this problem and the main thing I could think to google for was &#8220;background jobs ruby,&#8221; which led to a rash of articles about backgrounDRB and many many rubyists complaining about its many shortcomings and creating their own spin on what, at core is known as a message queue.  Many of the articles I read just did not cover the whole scope of the problem I faced and usually had simple solutions that lived well within the boundaries of their Rails (or Ramaze) framework&#8217;s inner workings.</p>
<p>Since I had a process that was capable of consuming resources upwards of two hours, I knew I had to completely escape the bounds of the framework and do the heavy lifting without fear that the parent process&#8217; lifespan would risk the job getting terminated mid-stream.  I also didn&#8217;t need a Rails plugin because, well, I&#8217;m building a Ramaze application and those fancy plugins just aren&#8217;t much of a help to me there.</p>
<p>One of the things I&#8217;m loving about Ramaze is that its forcing me to learn the Ruby language and its also forcing me to work harder to understand the components I&#8217;m pulling together in a solution.  With Rails, I just pulled a plugin out of thin air, plugged it in and moved along.  Which is great for rapid development, but more often than not, I have found myself later in a bind after the site&#8217;s live finding out that wonderful plugin is running way too slow and not knowing a darn thing about how to fix the problem without tearing up the whole plugin with a complete rewrite (in many cases).</p>
<p>With Ramaze, I feel much closer to the language, and I&#8217;m also finding that there&#8217;s just a <em>lot of fluff</em> I flat out don&#8217;t need that the Rails framework brings along, or rather, encourages developers to toss into the soup.  On the other hand, Ramaze&#8217;s philosophical approach of squeezing every ounce of functionality out of every line of code has really pushed me towards being a better Ruby programmer and yet the same old time crunch every nearly developer faces actually forces me to get the job done with less code and with utilizing only the critical components actually necessary to get the job done.  Which amounts to a one-two knockout punch for me because the solutions I deploy are consistently rock solid and I fully understand what I just put out there end-to-end.</p>
<h2>Getting Started</h2>
<p>The full project source can be found on <a href="http://github.com/mwlang/astroid">github.com</a> and you will also find my <a href="http://github.com/mwlang/ruby-progressbar">database enhancements to ProgressBar</a> on github.com as well.</p>
<p>This project utilizes the following:</p>
<ul>
<li><a href="http://www.ruby-lang.org">Ruby Language</a></li>
<li><a href="http://ramaze.net/">Ramaze</a></li>
<li><a href="http://kr.github.com/beanstalkd/">beanstalkd</a></li>
<li><a href="http://jquery.com">JQuery</a></li>
<li>A modified version of <a href="http://0xcc.net/ruby-progressbar/">ProgressBar</a> (included in the demo source)</li>
<li>These Gems
<ul>
<li><a href="http://raa.ruby-lang.org/project/daemons/">daemons</a></li>
<li><a href="http://flori.github.com/json/">json</a></li>
<li><a href="http://beanstalk.rubyforge.org/">beanstalk-client</a></li>
</ul>
</li>
</ul>
<h2>The Players</h2>
<p>With the ramblings out of the way, its time to get to the heart of the matter at hand.  There are three players in this game:</p>
<ol>
<li><em>The trigger</em> &#8211; This is the process that starts the whole chain reaction.  In this case, the web app where the user can click a link to start the export.  In my case, that web app was built with Ramaze.</li>
<li><em>The message queue</em> &#8211; This is the middleman in the game.  He sits around and accepts the message from trigger and passes the buck to the worker.  We call him, &#8220;beanstalkd.&#8221;</li>
<li><em>The worker</em> &#8211; This is the process doing all the work.  Amazingly, Ruby was up for the task and the daemons gem is the ticket to making this work.</li>
</ol>
<p>So lets cover these in more detail&#8230;</p>
<h2>The Worker (sort of&#8230;)</h2>
<p>The worker has got to always be around to do the job whenever that job comes in, so this suggested to me that I needed a daemonized process to watch the message queue for incoming jobs.  Many solutions that I saw called for simple cronjobs running every minute or 5 minutes or whatever.  But I couldn&#8217;t fathom why I&#8217;d want to resort to such hackery.  How are you going to scale that?  What happens if two cronjob instances fire and grab the same job request?  What about if the job crashes or the server dies in mid-job?  How is it reliably restarted?  How do I ensure long-running jobs don&#8217;t pile up and consume all memory and CPU resources (e.g. every minute start processing the next big job and next thing you know, ten of &#8216;em are running when you only have the capacity to run one or two)?  Simply put: cronjob workers is brute force mentality and rife with potential pitfalls.</p>
<p>Point being, I rapidly came to understand that I needed a message queue to serialize the jobs until the workers could take the jobs and run them and guarantee the jobs run before flushing them away.  What was surprising, was the number of message queue implementations out there in the wild!  Luckily, <a href="http://kr.github.com/beanstalkd/">beanstalkd</a> existed and had a very elegant implementation and API well-suited for Ruby.  I easily installed from source to a Ubuntu server and also installed via macports on my Macbook Pro laptop AND installed the ruby client AND tested the small demos in irb all in about 30 minutes.  Sadly I can&#8217;t say the same for RabbitMQ, Starling/Workling, ApacheMQ, and others I tried in vain to get up and going with.  So, beanstalkd won out by default.  Go to the <a href="http://kr.github.com/beanstalkd/">beanstalkd</a> website and follow their instructions.  Its <em>that</em> simple.</p>
<h3>How are we gonna keep up with the jobs?</h3>
<p>Although the message queue has a way of peeking into it to learn about number of jobs and what&#8217;s running and the status of said jobs, the background processes I was going to be doing was going to run long enough that I wanted to pass along exactly what stage in the process the job was crunching and this meant building periodic update logging into the actual background job itself.  The problem then is, how do we pass the information cleanly between worker and web app?</a></p>
<p>After some thinking on the matter, I chose to pass along progress updates via the database since all the headaches of I/O contention, persistence and deadlocking had already been solved there.  I wasn&#8217;t quite sure how I was going to format my status updates and post the info to the database until I thought about the great little ProgressBar gem that I was already using for the task when I kicked it off at console.  After a few minutes of looking into the internals of ProgressBar, I knew I could easily extend it to report its status to the database rather than emitting to the console.  An hour later, I had my DbProgressBar and another hour later I had my unit tests written and passing.</p>
<h2>Oh, yeah, the Worker&#8230;</h2>
<p>I digressed from talking about the worker to covering the message queue and passing messages around, so lets get back to that worker script.  As I pointed out earlier, I needed a worker script to do the heavy lifting for the job at hand and I wanted that puppy daemonized.  One thing was perfectly clear in my mind:  I didn&#8217;t want cronjobs nor <b>Threads</b> that depended on the parent processes staying alive (remember how Passenger aggressively scrubs ruby processes from memory!) nor any other means that depended on the parent process within the Ramaze framework kicking off the process directly.</p>
<p>The &#8220;daemons&#8221; rubygem fits the bill perfectly!  I was really surprised by this little gem of a gem. Daemons makes it extremely easy to wrap ordinary Ruby scripts in the usual <em>start|stop|restart|status</em> world you find inside the typcial /etc/init.d/ Linux folder.  What blew my mind was that I didn&#8217;t have to look at the usual long-winded case statement for all those daemonizing commands.  Just focus on writing the main task for the worker, wrap that task in an infinite loop, install it rc.d folders as appropriate and you&#8217;re pretty much done.  The following shows an example worker script:</p>
<pre class="twilight"><span class="Keyword">require</span> <span class="String"><span class="String">'</span>rubygems<span class="String">'</span></span>
<span class="Keyword">require</span> <span class="String"><span class="String">'</span>daemons<span class="String">'</span></span>
<span class="Keyword">require</span> <span class="String"><span class="String">'</span>beanstalk-client<span class="String">'</span></span>
<span class="Keyword">require</span> <span class="String"><span class="String">'</span>sequel<span class="String">'</span></span>

<span class="Variable">ROOT_DIR</span> <span class="Keyword">=</span> <span class="Support">File</span>.<span class="Entity">dirname</span>(<span class="Support">File</span>.<span class="Entity">expand_path</span>(<span class="Variable">__FILE__</span>))
<span class="Variable"><span class="Variable">$</span>LOAD_PATH</span>.<span class="Entity">unshift</span>(<span class="Support">File</span>.<span class="Entity">join</span>(<span class="Variable">ROOT_DIR</span>, <span class="String"><span class="String">'</span>lib<span class="String">'</span></span>))

<span class="Keyword">require</span> <span class="String"><span class="String">'</span>dbprogressbar<span class="String">'</span></span>
<span class="Keyword">require</span> <span class="String"><span class="String">'</span>sequel_output<span class="String">'</span></span>

<span class="Support">Daemons</span>.<span class="Entity">run_proc</span>(<span class="String"><span class="String">'</span>process_astroid_jobs<span class="String">'</span></span>, <span class="Constant"><span class="Constant">:</span>log_output</span> =&gt; <span class="Constant">true</span>) <span class="Keyword">do</span>
  <span class="Variable">DB</span> <span class="Keyword">=</span> <span class="Support">Sequel</span>.<span class="Entity">sqlite</span>(<span class="Support">File</span>.<span class="Entity">join</span>(<span class="Variable">ROOT_DIR</span>, <span class="String"><span class="String">'</span>demo.db<span class="String">'</span></span>))
  <span class="Support">SequelOutput</span>.<span class="Entity">prepare_database</span>(<span class="Variable">DB</span>)
  beanstalk <span class="Keyword">=</span> <span class="Support">Beanstalk</span>::<span class="Entity">Pool</span>.<span class="Entity">new</span>([<span class="String"><span class="String">&quot;</span>0.0.0.0:11300<span class="String">&quot;</span></span>])

  <span class="Keyword">loop</span> <span class="Keyword">do</span>
    job <span class="Keyword">=</span> beanstalk.<span class="Entity">reserve</span>
    jh <span class="Keyword">=</span> job.<span class="Entity">ybody</span>
    puts <span class="String"><span class="String">&quot;</span>processing <span class="StringEmbeddedSource"><span class="StringEmbeddedSource">#{</span>jh<span class="StringEmbeddedSource">[</span><span class="StringConstant"><span class="StringConstant">:</span>job_id</span><span class="StringEmbeddedSource">]</span><span class="StringEmbeddedSource">}</span></span><span class="String">&quot;</span></span>

    total <span class="Keyword">=</span> <span class="Constant">15</span>
    pbar <span class="Keyword">=</span> <span class="Support">DbProgressBar</span>.<span class="Entity">new</span>(<span class="String"><span class="String">&quot;</span>job #<span class="StringEmbeddedSource"><span class="StringEmbeddedSource">#{</span>jh<span class="StringEmbeddedSource">[</span><span class="StringConstant"><span class="StringConstant">:</span>job_id</span><span class="StringEmbeddedSource">]</span><span class="StringEmbeddedSource">}</span></span><span class="String">&quot;</span></span>, total,
      <span class="Support">SequelOutput</span>.<span class="Entity">new</span>(jh[<span class="Constant"><span class="Constant">:</span>job_id</span>], <span class="Variable">DB</span>))

    total.<span class="Entity">times</span> <span class="Keyword">do</span>
      pbar.<span class="Entity">inc</span>
      <span class="Entity">sleep</span>(<span class="Constant">1</span>)
    <span class="Keyword">end</span>

    pbar.<span class="Entity">finish</span>
    puts <span class="String"><span class="String">&quot;</span>finished <span class="StringEmbeddedSource"><span class="StringEmbeddedSource">#{</span>jh<span class="StringEmbeddedSource">[</span><span class="StringConstant"><span class="StringConstant">:</span>job_id</span><span class="StringEmbeddedSource">]</span><span class="StringEmbeddedSource">}</span></span><span class="String">&quot;</span></span>
    job.<span class="Entity">delete</span>
  <span class="Keyword">end</span>
<span class="Keyword">end</span>
</pre>
<p>There are a few key things to note about the above script.  Firstly, wrapping it in the Daemons.run_proc tells the daemons gem we want to run this like we&#8217;d run a typical server.  SequelOutput is the extension I wrote for ProgressBar to get it to outout to the database and at the start of the script, we ensure the database is ready (namely that the &#8220;progress_bars&#8221; table is created) before we go into our infinite loop to process jobs.</p>
<p>Next up, the loop itself.  Its not a runaway infinite loop that&#8217;ll end up consuming all resources because we are calling the beanstalk queue with &#8220;reserve&#8221; which is a blocking call, thus we sit and wait until a job comes through.  As soon as we get a new job request, we create the DbProgressBar and get to work processing&#8230;.twiddling our thumbs for 15 seconds in this case!  When the job&#8217;s completed, we remove it form the message queue and begin again.</p>
<h2>The Trigger</h2>
<p>Finally, with the back-end done, we could focus on enabling the user to trigger the job on the front-end.  Of course, that was as simple as firing up a connection to the beanstalk server and pushing a job onto the queue in a controller&#8217;s action.  But how do we watch the job and report to the user what&#8217;s going on without overwhelming our server?  The answer came along with a <a href="http://blog.purepistos.net/index.php/2009/01/27/comet-with-ramaze/">&#8220;Comet with Ramaze&#8221; example by Pistos</a>.  What a lovely idea:  Let the server intentionally hold onto the client&#8217;s request until it had something to report back!  I knew it&#8217;d never hold onto the request for more than 2 or 3 seconds, so this was a perfectly workable model for keeping traffic levels sane and timely.</p>
<p>This was also my first time playing around with JSON in AJAX calls and so I got to spend some time flexing my JQuery muscles a bit and also discovered the Ruby json gem that makes it dirt simple to turn a hash into a JSON response.  This is just a small excerpt from the controller/main.rb check_job_progress action:</p>
<pre class="twilight"><span class="Comment"><span class="Comment">#</span> report status as finished when the job is completed</span>
<span class="Keyword">if</span> progress[<span class="Constant"><span class="Constant">:</span>job_finish</span>]
  <span class="Keyword">return</span> {<span class="Constant"><span class="Constant">:</span>progress_text</span> =&gt; <span class="String"><span class="String">&quot;</span>finished.<span class="String">&quot;</span></span>, <span class="Constant"><span class="Constant">:</span>progress_status</span> =&gt; <span class="String"><span class="String">&quot;</span>finished<span class="String">&quot;</span></span>}.<span class="Entity">to_json</span>

<span class="Comment"><span class="Comment">#</span> otherwise report the percent done as the job status </span>
<span class="Keyword">else</span>
  <span class="Keyword">return</span> {<span class="Constant"><span class="Constant">:</span>progress_text</span> =&gt; progress[<span class="Constant"><span class="Constant">:</span>progress_text</span>].<span class="Entity">gsub</span>(<span class="String"><span class="String">'</span> <span class="String">'</span></span>, <span class="String"><span class="String">'</span>&amp;nbsp;<span class="String">'</span></span>),
      <span class="Constant"><span class="Constant">:</span>progress_status</span> =&gt; progress[<span class="Constant"><span class="Constant">:</span>progress_percent</span>]}.<span class="Entity">to_json</span>
<span class="Keyword">end</span>
</pre>
<p>Notice the call to the to_json to convert that hash to JSON response and how clean the Ruby code is even though we&#8217;re passing a rich data object back to our browser client.  I substituted blanks with the &amp;nbsp; character to keep the progress bar from collapsing all those empty blanks between the two vertical bars.  I decided to pass on a progress status so that I could check when things were done and thus stop polling the server for job updates.  This is just a demo, so try not to snicker too much at my use of plain English to convey critical job statuses.  At least the intent is clear!</p>
<p>The JQuery script is also correspondingly simple.</p>
<pre class="twilight"><span class="Storage">function</span> <span class="Entity">check_progress</span>() {
	job_progress_element <span class="Keyword">=</span> <span class="Keyword">$</span>(<span class="String"><span class="String">'</span>#job-progress<span class="String">'</span></span>);
	<span class="Keyword">$</span>.getJSON(<span class="String"><span class="String">'</span>/check_job_progress<span class="String">'</span></span>, {job_id: job_progress_element.attr(<span class="String"><span class="String">'</span>value<span class="String">'</span></span>)},
	  <span class="Storage">function</span>(data){
	    job_progress_element.html(data.progress_text);
      	<span class="Keyword">if</span> (data.progress_status <span class="Keyword">!</span><span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>finished<span class="String">&quot;</span></span>) check_progress();
	});
}

<span class="Keyword">$</span>(<span class="Support">document</span>).ready(<span class="Storage">function</span>() {
	<span class="Keyword">if</span>(<span class="Keyword">$</span>(<span class="String"><span class="String">'</span>#job-progress<span class="String">'</span></span>).<span class="SupportConstant">length</span>) check_progress();
});
</pre>
<p>As you can see, it simply asks the server for an update and when it receives one, updates itself and asks again!  It will check the job status after each update to see if the job has completed or not, thus stopping its persistent nagging once we&#8217;ve reached the Kingdom of Far, Far Away.</p>
<h3>References</h3>
<p>Here are a few other references, not mentioned above, that were invaluable in helping me pull this solution together.</p>
<p><a href="http://devver.net/blog/2008/10/ruby-beanstalkd-distributed-worker-basics/">Ruby Beanstalkd Distributed Worker Basics</a><br />
<a href="http://snippets.aktagon.com/snippets/212-How-to-create-a-daemon-process-using-Ruby-and-the-daemons-RubyGem">How to Create a Daemon Process Using Ruby and the Daemons RubyGem</a><br />
<a href="http://nubyonrails.com/articles/about-this-blog-beanstalk-messaging-queue">Err the Blog &#8211; About this blog Beanstalk Messaging Queue&#8221;&gt;</a></p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/implementing-ruby-jobs-in-the-background/38/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>What makes a top award candidate, anyhow?</title>
		<link>http://ramblings.gibberishcode.net/archives/what-makes-a-top-award-candidate-anyhow/37</link>
		<comments>http://ramblings.gibberishcode.net/archives/what-makes-a-top-award-candidate-anyhow/37#comments</comments>
		<pubDate>Sat, 01 Aug 2009 16:30:04 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Ruby Language]]></category>
		<category><![CDATA[imagemagick]]></category>
		<category><![CDATA[inkscape]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ramaze]]></category>
		<category><![CDATA[sequel]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=37</guid>
		<description><![CDATA[In our local Linux user group mailing list, one of the participant asked the group for recommendations for open source software projects that should be considered for a prominent publication&#8217;s top 100 awards.  The ensuing discussion got me to wondering:  What is the purpose behind doing the awards?  Most of what I [...]]]></description>
			<content:encoded><![CDATA[<p>In our local Linux user group mailing list, one of the participant asked the group for recommendations for open source software projects that should be considered for a prominent publication&#8217;s top 100 awards.  The ensuing discussion got me to wondering:  What is the purpose behind doing the awards?  Most of what I see talked about are all well-known and well-used, such as git, mercurial, word press, joomla, django, and so on.  Usually, when I look at a list like this, I&#8217;m looking to uncover new and undiscovered projects that have real potential to make a real impact in my daily life and thus was somewhat surprised that such mature projects are being so heavily promoted in the discussions.
</p>
<p>Its not that I don&#8217;t think these tools are worthy contenders.  Most of them, I have used fairly extensively myself over the years.  The suggested contenders are solid and mature projects, but they feel decidedly &#8220;old hat&#8221; to me much as apache, postfix, vim, bind, and the linux kernel probably feels to the system administrator.  In other words, they&#8217;re ubiquitous, so why signal them out with some sort of top 100 reward?
</p>
<p>These days, I have adopted Ruby as my language of choice, so I have moved away from Python and PHP.  While this has largely meant I develop Ruby on Rails applications.  However, as my Ruby skills grow, this also means going far and wide in many other ruby-based tools (such as rake, sake, and rack for system-level or middleware tools).  I am now using ruby for quick and dirty systems administration tasks (such as the short script I wrote to find all currently assigned IP addresses on our office network), automating deployments (capistrano, puppet, chef), marshaling data from one database system to another, and even with extensive image-processing (resizing, conversion of formats, compressing, etc.).
</p>
<p>So what, in my view, warrants a top 100 award recognition?  I like to see new and exciting projects on these lists.  If a project&#8217;s on the list, I like to know that they are extremely active and rapidly gaining mind-share and penetration into the community.  I want to know a little bit about how they put a new spin on an old problem and the impact they have been able to make, not only in the project&#8217;s direct vertical niche, but across the board.  A few years ago, Ruby on Rails arrived on the scene as a game-changer, not only for putting Ruby on the map as a viable language, but as a bona fide development framework for delivering web-based services.  Ruby on Rails turned the Java world on its head and forced Java developers to drastically improve their productivity tools then in common practice; they evangelized convention over configuration and brought many fantastic ideals such as Agile development and RESTful APIs to the forefront of many, many developers&#8217; minds.
</p>
<p>To my way of thinking, though, Ruby on Rails is now old news.  Newcomers will stumble upon Ruby and Rails through word of mouth or simply googling for a solution to some problem they&#8217;re trying to solve.  That&#8217;s the problem I see with top-100 lists paying tribute year after year to the same old mature projects.  Whether its Ruby on Rails or Word Press or Joomla or Django, all of these are easily discovered and well-written about by anybody with decent querying skills.  So what&#8217;s out there today that&#8217;s refreshing and hard to find? Yet, is making a huge impact on the community?  Here are a few that I think are worthy contenders (and yes, they are a little heavy on the Ruby side)&#8230;
</p>
<h2>Rack</h2>
<p><a href="http://rack.rubyforge.org/">Rack: a Ruby Webserver Interface</a>.  What I find phenomenal about Rack is its adoption by so many frameworks that are delivering web-based services.  Micro-frameworks like Ramaze and Camping and Sinatra were early adopters of Rack and such adoption led to far smaller code-bases in each as well as significant performance boosts and, as if that wasn&#8217;t enough, enabled much easier transition from Ruby 1.8 to 1.9.
</p>
<h2>Inkscape and ImageMagick</h2>
<p>Speaking of image processing, I am not sure if many have encountered <a href="http://www.imagemagick.org/script/index.php">ImageMagick</a> and the Ruby bindings offered via <a href="http://rmagick.rubyforge.org/">RMagick</a>, but definitely worth a look-see.  As of this year, I do almost all of my creative graphics work in <a href="http://www.inkscape.org">Inkscape</a> and <a href="http://www.gimp.org/">The Gimp</a> and use ImageMagick to automate some pretty impressive transformations of those images outside the GUI.  Before Inkscape became mature enough to use on daily basis (which, by my reckoning is only in the last 18 months or so), I was heavily reliant on Adobe&#8217;s Illustrator and companion Photoshop apps.  I never would&#8217;ve imagined two years ago that I could completely do away with these packages, let alone begin to automate much of the repetitive tasks that I put those resulting images through (resizing, cropping, web-optimizing, etc.) with a collection of well-written OSS tools.
</p>
<h2>Ramaze and Sequel</h2>
<p>One last pair of tools I think is worthy of consideration, especially for web-based ruby developers is <a href="http://sequel.rubyforge.org/">Sequel</a> and <a href="http://ramaze.net/">Ramaze</a>. Sequel is an object-relational-mapping library that makes working with SQL databases a real pleasure for Ruby developers.  The maintainers seem to have taken the best of the best and rolled those ideas into one coherent and extremely fast library for accessing just about any database out there.  I have fooled around with DataMapper and have worked extensively with ActiveRecord and I must say Sequel really is rung or two above these libraries and I look forward to continuing to adopt Sequel.
</p>
<p>I ran into Sequel by way of the Ramaze project, which is a micro-framework for building web-based applications.  Unlike Ruby on Rails, which tends to generate tons of scaffold/skeletal code for you to begin hacking away on to build your projects, Ramaze takes the opposite tack in that it provides little to no scaffolding or auto-generated code.  Instead, the mindset is squeezing the maximum amount of functionality out of every line of code you do write.  For a Ruby on Rails developer coming to Ramaze, the feeling is very much like the first time I took off on a bicycle without the training wheels attached.  You feel wildly uncontrolled and like you may fall at any moment, but a little encouragement from the warm friendly community gets you going straight and true and the amount of code you&#8217;re NOT writing, reading, reviewing, nor specking tests for really begins to jump out at you!</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/what-makes-a-top-award-candidate-anyhow/37/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache 2.2 and Active Directory and Group restrictions</title>
		<link>http://ramblings.gibberishcode.net/archives/apache-22-and-active-directory-and-group-restrictions/36</link>
		<comments>http://ramblings.gibberishcode.net/archives/apache-22-and-active-directory-and-group-restrictions/36#comments</comments>
		<pubDate>Wed, 22 Jul 2009 16:44:44 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Configuration]]></category>
		<category><![CDATA[Servers]]></category>
		<category><![CDATA[Setups]]></category>
		<category><![CDATA[Systems]]></category>
		<category><![CDATA[active directory]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[authnz ldap module]]></category>
		<category><![CDATA[mod_authnz_ldap]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=36</guid>
		<description><![CDATA[I was really struggling with getting Apache and Active Directory talking and restricting access (authorizing) only a sub-set of users that belonged to a particular group.  Here, I show you how I solved the problem.

The Stage
I am setting up a Ubuntu server with Apache 2.2 and authenticating users against a Windows 2003 Server&#8217;s Active [...]]]></description>
			<content:encoded><![CDATA[<p>I was really struggling with getting Apache and Active Directory talking and restricting access (authorizing) only a sub-set of users that belonged to a particular group.  Here, I show you how I solved the problem.
</p>
<h2>The Stage</h2>
<p>I am setting up a Ubuntu server with Apache 2.2 and authenticating users against a Windows 2003 Server&#8217;s Active Directory.  This is standard fare when all you need is a require valid-user directive.  But when it came to getting require ldap-group directive going, I really stumbled.  And on multiple fronts, too.  I am not a Windows guru by any measure, nor am I an LDAP guru, so there was a huge cultural and vocabulary barrier for me to cross on both the LDAP vernacular and the somewhat different parlance surrounding Microsoft&#8217;s documentation on Active Directory.  Its like a Southern Redneck talking to a Scottish Highlander about home-brewing beer.
</p>
<p>So here&#8217;s what we&#8217;re going to do:</p>
<ul>
<li>Apache 2.2</li>
<li>Microsoft Active Directory (Windows 2003 Server)</li>
<li>authnz_ldap module</li>
</ul>
<p>For this effort, I am authenticating users that reside in the MyBusiness -> Users folders and I&#8217;m restricting access to the Auditors Security Group, also residing under MyBusiness.  The domain I use is &#8220;sample.local&#8221; herein.  See the screen shot of the Active Directory structure and details of the Auditors group below:
</p>
<p><img src="http://www.cybrains.net/images/ad_screenshot.png" alt='Active Directory Screenshot' /></p>
<p>There are <a href="http://httpd.apache.org/docs/trunk/mod/mod_authnz_ldap.html">plenty of resources out there</a> about getting the authnz_ldap module working with Apache, so I won&#8217;t be covering this part of the setup.  Instead, I will focus on the gestalt of the actual configuration.
</p>
<h2>Will the Real AuthLdapUrl Please Stand Up?</h2>
<p>There seems to be no real standard way to set the AuthLdapURL configuration string.  I think I visited ten or fifteen web sites on the topic and each one was a little bit different!  And none really explained fully the chosen syntax and LDAP selectors being used.  Fortunately, I came across a <a href="http://www.computerperformance.co.uk/Logon/Logon_CSVDE.htm">site dedicated to explaining CSVDE</a>, which turned out to be a bit of a rosetta stone for me.
</p>
<p>CSVDE is a tool I had never heard of, but yet is apparently installed on every Windows 2003 Server.  After about five minutes of reading and 20 minutes of perusing the CSV export I generated, I knew exactly how to construct the AuthLdapURL configuration string.  Well, with one caveat, that is.  I still am not sure what &#8220;(objectClass=*)&#8221; does for me, but several examples had it and I kept it.
</p>
<h2>Know your Users</h2>
<p>All of my users are maintained under group policies and they reside under the domain&#8217;s root node in &#8220;MyBusiness&#8221; and &#8220;Users&#8221; under that.  Standard Windows accounts, especially system administrator accounts reside in a &#8220;Users&#8221; folder directly off the domain&#8217;s root node.  This was one of the first points of contention that I had to understand when looking at the vast majority of other people&#8217;s solution.  Simply put, know which &#8220;Users&#8221; you wish to work with in Apache.
</p>
<p>Knowing I wanted to authenticate users under the MyBusiness node, my AuthLdapUrl becomes:
</p>
<pre class="twilight">
AuthLDAPURL ldap://ad.sample.local:389/OU=MyBusiness,DC=sample,DC=local?sAMAccountName?sub?(objectClass=*)
</pre>
<h2>Bind a User so Apache can Query</h2>
<p>You do have to bind to Active Directory so that Apache can actually query.  To do this, I created a User under the standard &#8220;Users&#8221; folder (where administrator, and other such accounts traditionally reside).  I don&#8217;t know if I applied correct rights or not, but the account is no more than a &#8220;Domain User&#8221; member.  I called the user &#8220;AD_VIEWER&#8221; with description &#8220;Account to bind Apache to Active Directory for authentication.&#8221;  This leads to the following Apache directives:
</p>
<pre class="twilight">
AuthLDAPBindDN "AD_VIEWER@sample.local"
AuthLDAPBindPassword bottom_secret_password
</pre>
<p>I tested the above getting the usual &#8220;require valid-user&#8221; directive working with Apache.  Once I was sure I could do this much, it was time to turn my attention to restricting access to users that are members of a specific group.
</p>
<h2>Where&#8217;s my Group?</h2>
<p>I struggled for quite a while with the groups before I finally got wise and set the debug level to get more details into the apache log files.  The debug logging level can be set with:
</p>
<pre class="twilight">
LogLevel debug
</pre>
<p>And this leads to quite detailed information about what&#8217;s going on with Apache and Authentication.  For example, before I got my ldap-group configuration correct, I was seeing this:
</p>
<pre class="twilight">
[debug] mod_authnz_ldap.c(721): [2934] auth_ldap authorise: require group: testing for uniquemember: CN=Code Connoisseur,OU=MyBusiness,OU=Users,DC=sample,DC=local (CN=Auditors,OU=MyBusiness,DC=sample,DC=local)
[debug] mod_authnz_ldap.c(737): [2934] auth_ldap authorise: require group "CN=Auditors,OU=MyBusiness,DC=sample,DC=local": authorisation failed [Comparison complete][No such object]
[debug] mod_authnz_ldap.c(852): [2934] auth_ldap authorise: authorisation denied
</pre>
<p>Note the &#8220;No such object&#8221; as this was telling me the group itself was not found.  I tried many permutations without success until I found the CSVDE program and was able to dump the Active Directory to Excel for further inspection.  I realized with the CSVDE output that I was doing both the<br />
AuthLDAPURL (the above AuthLDAPURL is correct as I have spared you from my mistakes).  The correct ldap-group config string for accessing a Security Group defined under MyBusiness is:</p>
<pre class="twilight">
require ldap-group CN=Auditors,OU=Security Groups,OU=MyBusiness,DC=sample,DC=local
</pre>
<p>In short, you have to exactly declare the selector for your Active Directory Group or Apache will fail to find it.  After getting the group setting right, I now see &#8220;attribute member&#8221; and &#8220;Comparison true&#8221; in the logs, which indicates that the group was found and that my user is a member of that group.
</p>
<pre class="twilight">
[debug] mod_authnz_ldap.c(377): [3134] auth_ldap authenticate: using URL ldap://10.0.1.5:389/OU=MyBusiness,DC=sample,DC=local?sAMAccountName?sub?(objectClass=*)
[debug] mod_authnz_ldap.c(474): [3134] auth_ldap authenticate: accepting mwlang
[debug] mod_authnz_ldap.c(715): [3134] auth_ldap authorise: require group: testing for group membership in "CN=Auditors,OU=Security Groups,OU=MyBusiness,DC=sample,DC=local"
[debug] mod_authnz_ldap.c(721): [3134] auth_ldap authorise: require group: testing for member: CN=Michael Lang,OU=Employees,OU=Users,OU=Users,OU=MyBusiness,DC=sample,DC=local (CN=Auditors,OU=Security Groups,OU=MyBusiness,DC=sample,DC=local)
[debug] mod_authnz_ldap.c(730): [3134] auth_ldap authorise: require group: authorisation successful (attribute member) [Comparison true (adding to cache)][Compare True]
</pre>
<h2>The complete Apache Config</h2>
<p>Bringing all of the items discussed above together, we arrive at this Apache configuration.  I am using Virtual Hosts to uniquely identify the service.  Also of note, I am using Ramaze to provide the functionality of the actual site, so please do take this into account if you&#8217;re copying and pasting.  This config is a complete virtual host entry:
</p>
<pre class="twilight"><span class="Keyword">&lt;</span>VirtualHost *:<span class="Keyword">80&gt;</span>
    ServerName audit.sample.local
    RackBaseURI /
    RackAutoDetect on
    DocumentRoot /var/www/balancer/public

    LogLevel debug
    <span class="Keyword">&lt;</span>Location <span class="String"><span class="String">&quot;</span>/data_entry<span class="String">&quot;</span></span><span class="Keyword">&gt;</span>
        AuthBasicProvider ldap
        AuthType Basic
        AuthName <span class="String"><span class="String">&quot;</span>Sample Realm<span class="String">&quot;</span></span>
        AuthLDAPURL ldap://10.0.1.5:389/OU=MyBusiness,DC=sample,DC=local?sAMAccountName?sub?(objectClass=*)
        AuthzLDAPAuthoritative on

        AuthLDAPBindDN <span class="String"><span class="String">&quot;</span>AD_VIEWER@sample.local<span class="String">&quot;</span></span>
        AuthLDAPBindPassword bottom_secret_password

        AuthLDAPGroupAttributeIsDN on
        require ldap-group CN=Auditors,OU=Security Groups,OU=MyBusiness,DC=sample,DC=local
    <span class="Keyword">&lt;</span>/Location<span class="Keyword">&gt;</span>
<span class="Keyword">&lt;</span>/VirtualHost<span class="Keyword">&gt;</span>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/apache-22-and-active-directory-and-group-restrictions/36/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>macports is not darwinports</title>
		<link>http://ramblings.gibberishcode.net/archives/macports-is-not-darwinports/35</link>
		<comments>http://ramblings.gibberishcode.net/archives/macports-is-not-darwinports/35#comments</comments>
		<pubDate>Tue, 23 Jun 2009 00:36:06 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Macs]]></category>
		<category><![CDATA[Setups]]></category>
		<category><![CDATA[Systems]]></category>
		<category><![CDATA[darwinports]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[macports]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=35</guid>
		<description><![CDATA[For those of you who don&#8217;t keep a close eye on the macports system that provides you with convenient builds of popular open source software, let me put you on guard:  macports supplanted darwinports a few years back to avoid continued confusion over the name.
I was having a good bit of trouble getting ports [...]]]></description>
			<content:encoded><![CDATA[<p>For those of you who don&#8217;t keep a close eye on the macports system that provides you with convenient builds of popular open source software, let me put you on guard:  macports supplanted darwinports a few years back to <a href="http://trac.macports.org/wiki/DarwinPorts">avoid continued confusion</a> over the name.</p>
<p>I was having a good bit of trouble getting ports to install ImageMagick and have had trouble many other times getting ports to install things.  As such, I often fell back on fink or compiling and installing from source rather than figuring out the root causes of my macports problems.  </p>
<p>Macports was surprisingly easy to fix up and get fully operational again, and this was on a fairly old machine that I am pretty certain I started with &#8220;Darwin Ports&#8221; roughly 3.5 years ago.  </p>
<h2>First Thing&#8217;s First</h2>
<p>First, know that the official macports website is: <a href="http://macports.org">trac.macports.org</a>.  Be wary of anything &#8220;darwinports&#8221; as name is officially retired in favor of &#8220;macports.&#8221;
</p>
<h2>Trouble a-brewing</h2>
<p>It all started innocently enough.  I wanted to install ImageMagick on my mac.  I&#8217;ve been through installing ImageMagick on Linux and knew it was not going to be a cakewalk to install from source.  ImageMagick simply has too many dependencies to relish the thought of an install from source route.  So I googled and found <a href="">instructions for ports</a> and began:
</p>
<pre class="twilight">sudo port install tiff -macosx imagemagick +q8 +gs +wmf
Warning: Group file could not be located.
---<span class="Keyword">&gt;</span>  Activating tiff 3.8.2_3
Error: Target org.macports.activate returned: Image error: Another version of this port (tiff @3.8.2_2+darwin_9+macosx) is already active.
</pre>
<p>Oh, ok, lets remove the old one&#8230;</p>
<pre class="twilight">
sudo port uninstall tiff -macosx imagemagick +q8 +gs +wmf
---<span class="Keyword">&gt;</span>  The following versions of tiff are currently installed:
---<span class="Keyword">&gt;</span>  	tiff @3.8.2_2+darwin_9+macosx (active)
---<span class="Keyword">&gt;</span>  	tiff @3.8.2_3
Error: port uninstall failed: Registry error: Please specify the full version as recorded <span class="Keyword">in</span> the port registry.
[mwlang@macdoze shared] sudo port uninstall tiff @3.8.2_2+darwin_9+macosx
---<span class="Keyword">&gt;</span>  Unable to uninstall tiff 3.8.2_2+darwin_9+macosx, the following ports depend on it:
---<span class="Keyword">&gt;</span>  	gtk2
---<span class="Keyword">&gt;</span>  	lcms
---<span class="Keyword">&gt;</span>  	sane-backends
---<span class="Keyword">&gt;</span>  	xsane
Error: port uninstall failed: Please uninstall the ports that depend on tiff first.
[mwlang@macdoze shared] sudo port uninstall tiff @3.8.2_2+darwin_9+macosx gtk2 lcms sane-backends xsane
---<span class="Keyword">&gt;</span>  Unable to uninstall tiff 3.8.2_2+darwin_9+macosx, the following ports depend on it:
---<span class="Keyword">&gt;</span>  	gtk2
---<span class="Keyword">&gt;</span>  	lcms
---<span class="Keyword">&gt;</span>  	sane-backends
---<span class="Keyword">&gt;</span>  	xsane
Error: port uninstall failed: Please uninstall the ports that depend on tiff first.
[mwlang@macdoze shared] sudo port uninstall gtk2 lcms sane-backends xsane
---<span class="Keyword">&gt;</span>  Unable to uninstall gtk2 2.12.9_0+x11, the following ports depend on it:
---<span class="Keyword">&gt;</span>  	gconf
---<span class="Keyword">&gt;</span>  	gnome-keyring
---<span class="Keyword">&gt;</span>  	libglade2
---<span class="Keyword">&gt;</span>  	gail
---<span class="Keyword">&gt;</span>  	libgnomecanvas
---<span class="Keyword">&gt;</span>  	libbonoboui
---<span class="Keyword">&gt;</span>  	poppler
---<span class="Keyword">&gt;</span>  	py25-gtk
---<span class="Keyword">&gt;</span>  	gtk-nodoka-engine
---<span class="Keyword">&gt;</span>  	xsane
Error: port uninstall failed: Please uninstall the ports that depend on gtk2 first.
</pre>
<p>Not a very auspicious start, is it?  Time to step back and think about this one and dig into the macports facility to learn how to properly manage things.  New strategy: Get an education.</p>
<h2>Discovering Deactivate/Activate</h2>
<p>So, looking at the above problem, I realized that there was almost certainly an easier route out of this dependency nightmare and I needed to learn how to upgrade those old, stale packages.  Going through the docs reveals activate and deactivate commands.  Sounds perfect, lets try:</p>
<pre class="twilight">sudo port activate tiff @3.8.2_3
---<span class="Keyword">&gt;</span>  Activating tiff @3.8.2_3
Error: port activate failed: Image error: Another version of this port (tiff @3.8.2_2+darwin_9+macosx) is already active.
[mwlang@macdoze shared] sudo port deactivate tiff @3.8.2_2+darwin_9+macosx
---<span class="Keyword">&gt;</span>  Deactivating tiff @3.8.2_2+darwin_9+macosx
[mwlang@macdoze shared] sudo port activate tiff @3.8.2_3
---<span class="Keyword">&gt;</span>  Activating tiff @3.8.2_3
</pre>
<p>So there&#8217;s the kicker.  First, deactivate the old version, <i>then</i> activate the new version.</p>
<h2>So Macports itself is out of date, you say?</h2>
<pre class="twilight">sudo port install tiff -macosx imagemagick +q8 +gs +wmf
Error: Unable to execute port: invalid command name <span class="String"><span class="String">&quot;</span>use_autoreconf<span class="String">&quot;</span></span>
</pre>
<p>I had no clue what this was.  Googling around was turning up precious little.  So I hopped over into the IRC #macports channel.  Right off the bat, Toby tells me to selfupdate.  I had made the mistake of thinking that &#8220;sudo port sync&#8221; was keeping macports packages all up to date in one fell swoop.  But the two are two distinct activities.  So remember to keep ports itself updated with this:</p>
<pre class="twilight">sudo port selfupdate
</pre>
<p>With that, port was upgraded from a very old version to a bright and shiny new version.  Time to try to install ImageMagick again.  And I&#8217;m happy to report that I got much further along this time.  But still hit a roadblock&#8230;</p>
<h2>Mysterious failure demystified</h2>
<p>A little down the chain of installing various xorg packages, I hit upon this one with the xorg-libx11 package:</p>
<pre class="twilight">checking <span class="Keyword">for</span> XPROTO... configure: error: Package requirements (xproto <span class="Keyword">&gt;</span>= 7.0.13) were not met.
Consider adjusting the PKG_CONFIG_PATH environment variable <span class="Keyword">if</span> you
installed software <span class="Keyword">in</span> a non-standard prefix.

Alternatively you may set the XPROTO_CFLAGS and XPROTO_LIBS environment variables
to avoid the need to call pkg-config.  See the pkg-config man page <span class="Keyword">for</span>
more details.

Error: The following dependencies failed to build: ghostscript xorg-libXext xorg-libX11 xorg-libXt xorg-libsm xorg-libice
Error: Status 1 encountered during processing.
[mwlang@macdoze shared] sudo port install ImageMagick
Error: Requested variants <span class="Keyword">do</span> not match original selection.
Please perform <span class="String"><span class="String">'</span>port clean ImageMagick<span class="String">'</span></span> or specify the force option.
Error: Status 1 encountered during processing.
</pre>
<p>Strange, what&#8217;s that <b>xproto >= 7.0.13</b>?  How odd that a dependency would fail!  I tried a <b>sudo port install xproto</b>, but that was a no-go (so don&#8217;t bother trying, it&#8217;ll fail for you, too).  but then I noticed the dependency list (which oddly is missing <b>xorg-xproto</b>) and most of the packages are prefixed with &#8220;xorg-&#8221;, and tried <b>sudo port install xorg-xproto</b> and thus, was on my way again.</p>
<p>I lie! <i>I lie!</i>  I had no clue.  I googled &#8220;xproto port install&#8221; and learned the truth for the real package name that way.  However, as it happens, I found the answer on darwinports dot com in a dependency list for xorg-libx11.  I casually mentioned this site in the IRC channel and the #macports tenants were very quick to point out was an imposter site, so be wary of anything you read and learn there.  Information is liable to be stale while the site owner attempts to lighten your wallet a bit for no good cause.  Little did I know!</p>
<h2>Final tidbit</h2>
<p>Once I got the ports system updated and the xproto package installed, it was a fair breeze to finish with the ImageMagick installation.  One last command I learned from the good folks in #macports is to run the following on a frequent basis to keep your macports installation in fair good shape:</p>
<pre class="twilight">sudo port selfupdate <span class="Keyword">&amp;&amp;</span> sudo port upgrade outdated
</pre>
<h2>Conclusion</h2>
<p>For years,  I struggled with ports, and for no good reason.  Once I took the time to learn the package management&#8217;s commands (along with the help of those in #macports, for which I&#8217;m very grateful to them for saving me a few hours of head-scratching), I am finding I&#8217;m able to install packages painlessly on the mac and have even tried a couple of others I had problems with in the past and they&#8217;re all installing without a hitch.  The lesson for me:  take the time to learn your tools.  It may be slow-going at first, but the rewards always seem to pay back big dividends down the road.</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/macports-is-not-darwinports/35/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>One ActiveRecord Model Acting as a List and Tree</title>
		<link>http://ramblings.gibberishcode.net/archives/one-activerecord-model-acting-as-a-list-and-tree/33</link>
		<comments>http://ramblings.gibberishcode.net/archives/one-activerecord-model-acting-as-a-list-and-tree/33#comments</comments>
		<pubDate>Thu, 30 Apr 2009 21:44:56 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby Language]]></category>
		<category><![CDATA[activerecord]]></category>
		<category><![CDATA[acts_as_list]]></category>
		<category><![CDATA[acts_as_tree]]></category>
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=33</guid>
		<description><![CDATA[Occasionally, Rails can appear to make your life extremely easy while silently throwing you a curve-ball.  I needed a model that required a hierarchy while also preserving order of the records.  Although fairly straightforward to set up and start immediately using, there are a couple of &#8220;gotchas&#8221; to watch out for and this [...]]]></description>
			<content:encoded><![CDATA[<p>Occasionally, Rails can appear to make your life extremely easy while silently throwing you a curve-ball.  I needed a model that required a hierarchy while also preserving order of the records.  Although fairly straightforward to set up and start immediately using, there are a couple of &#8220;gotchas&#8221; to watch out for and this article covers those pitfalls and shows how to apply the cool new &#8220;dirty attributes&#8221; feature in ActiveRecord.
</p>
<h2>The Problem</h2>
<p>I am working on a content management system (CMS) where I want the pages to have a hierarchical structure that turns into a menu with sub-menus.  The content manager needs to also be able to order these pages so that the menu structure renders in the desired order.
</p>
<h2>The Solution</h2>
<p>Two plugins jumped to mind almost immediately:  <b>acts_as_tree</b> and <b>acts_as_list</b>.  The tree plugin will manage the hierarchy, hinging off the parent_id field of the model whilist the list plugin uses the position column to manage the order.  What&#8217;s unique here is that I have never used both on one model, but doing so was surprisingly easy:
</p>
<pre class="twilight"><span class="line-numbers">   1 </span> <span class="Keyword">class</span> <span class="Entity">Page<span class="EntityInheritedClass"> <span class="EntityInheritedClass">&lt;</span> ActiveRecord::Base</span></span>
<span class="line-numbers">   2 </span>   acts_as_tree
<span class="line-numbers">   3 </span>   acts_as_list
<span class="line-numbers">   4 </span> <span class="Keyword">end</span>
</pre>
<h2>Constructing a Hierarchical Menu</h2>
<p>Before going too far, if you haven&#8217;t seen the <a href="http://www.alistapart.com/articles/dropdowns">Suckerfish menus</a>, yet, please do check out the article as it will help you quickly see how I approached the menu rendering.  Secondly, to install the two plugins, its a simple pair of command line calls as follows:</p>
<pre class="twilight"><span class="line-numbers">   1 </span> script/plugin install acts_as_tree
<span class="line-numbers">   2 </span> script/plugin install acts_as_list
</pre>
<p>With the CSS handling all the styling, all I needed was to render the nested unordered lists.  I began by grabbing all of the pages at the top level (where parent_id is null) with this bit of code:
</p>
<pre class="twilight"><span class="line-numbers">   1 </span> <span class="Keyword">class</span> <span class="Entity">Page<span class="EntityInheritedClass"> <span class="EntityInheritedClass">&lt;</span> ActiveRecord::Base</span></span>
<span class="line-numbers">   2 </span>   acts_as_tree <span class="Constant"><span class="Constant">:</span>order</span> =&gt; <span class="Constant"><span class="Constant">:</span>position</span>
<span class="line-numbers">   3 </span>   acts_as_list
<span class="line-numbers">   4 </span>
<span class="line-numbers">   5 </span>   <span class="Keyword">def</span> <span class="Entity">self.top_level_pages</span>
<span class="line-numbers">   6 </span>     <span class="Entity">find</span>(<span class="Constant"><span class="Constant">:</span>all</span>, <span class="Constant"><span class="Constant">:</span>conditions</span> =&gt; [<span class="String"><span class="String">&quot;</span>parent_id IS NULL<span class="String">&quot;</span></span>], <span class="Constant"><span class="Constant">:</span>order</span> =&gt; <span class="Constant"><span class="Constant">:</span>position</span>)
<span class="line-numbers">   7 </span>   <span class="Keyword">end</span>
<span class="line-numbers">   8 </span> <span class="Keyword">end</span>
</pre>
<p>If you noticed the &#8220;:order => :position&#8221; clause and thought, &#8220;but acts_as_list handles that for you,&#8221; then you have spotted the first &#8220;gotcha&#8221; I encountered with using both tree and list on a model.  The tree plugin loses the position ordering that the list plugin mixes in and adding these order clauses in preserves the order of the records.  With the query to get the top-level menus in place, I set the @pages variable by calling Page.top_level_pages in the controller and then rendered with this call in my view:
</p>
<pre class="twilight"><span class="line-numbers">   1 </span> <span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">div</span> <span class="MetaTagAll"><span class="MetaTagAll">id</span><span class="MetaTagAll">=</span><span class="String"><span class="String">&quot;</span><span class="String">site-navigation</span><span class="String">&quot;</span></span></span><span class="MetaTagAll">&gt;</span></span>
<span class="line-numbers">   2 </span> 	<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">ul</span><span class="MetaTagAll">&gt;</span></span>
<span class="line-numbers">   3 </span> 		<span class="EmbeddedSourceBright"><span class="EmbeddedSourceBright">&lt;%=</span> <span class="SupportFunction">render</span><span class="EmbeddedSourceBright">(</span><span class="Constant"><span class="Constant">:</span>partial</span> <span class="EmbeddedSourceBright">=&gt;</span> <span class="String"><span class="String">&quot;</span>/layouts/site_navigation<span class="String">&quot;</span></span><span class="EmbeddedSourceBright">,</span> <span class="Constant"><span class="Constant">:</span>collection</span> <span class="EmbeddedSourceBright">=&gt;</span> <span class="Variable"><span class="Variable">@</span>pages</span><span class="EmbeddedSourceBright">)</span> <span class="EmbeddedSourceBright">%&gt;</span></span>
<span class="line-numbers">   4 </span> 	<span class="MetaTagAll"><span class="MetaTagAll">&lt;/</span><span class="MetaTagAll">ul</span><span class="MetaTagAll">&gt;</span></span>
<span class="line-numbers">   5 </span> <span class="MetaTagAll"><span class="MetaTagAll">&lt;/</span><span class="MetaTagAll">div</span><span class="MetaTagAll">&gt;</span></span>
</pre>
<p>To make it all nice and nested, I simply recursively called the same site_navigation partial on the children like so:
</p>
<pre class="twilight"><span class="line-numbers">   1 </span> <span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">li</span><span class="MetaTagInline">&gt;</span></span><span class="EmbeddedSourceBright"><span class="EmbeddedSourceBright">&lt;%=</span> <span class="EmbeddedSourceBright"><span class="Entity">link_to</span></span><span class="EmbeddedSourceBright">(</span>site_navigation<span class="EmbeddedSourceBright"><span class="EmbeddedSourceBright">.</span><span class="Entity">name</span></span><span class="EmbeddedSourceBright">,</span> <span class="SupportFunction">url_for</span><span class="EmbeddedSourceBright">(</span>site_navigation<span class="EmbeddedSourceBright">)</span><span class="EmbeddedSourceBright">)</span>  <span class="EmbeddedSourceBright">%&gt;</span></span><span class="MetaTagInline"><span class="MetaTagInline">&lt;/</span><span class="MetaTagInline">li</span><span class="MetaTagInline">&gt;</span></span>
<span class="line-numbers">   2 </span> <span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">ul</span><span class="MetaTagAll">&gt;</span></span><span class="EmbeddedSourceBright"><span class="EmbeddedSourceBright">&lt;%=</span> <span class="SupportFunction">render</span><span class="EmbeddedSourceBright">(</span><span class="Constant"><span class="Constant">:</span>partial</span> <span class="EmbeddedSourceBright">=&gt;</span> <span class="String"><span class="String">&quot;</span>/layouts/site_navigation<span class="String">&quot;</span></span><span class="EmbeddedSourceBright">,</span> <span class="Constant"><span class="Constant">:</span>collection</span> <span class="EmbeddedSourceBright">=&gt;</span> site_navigation<span class="EmbeddedSourceBright"><span class="EmbeddedSourceBright">.</span><span class="Entity">children</span></span><span class="EmbeddedSourceBright">)</span> <span class="EmbeddedSourceBright">%&gt;</span></span><span class="MetaTagAll"><span class="MetaTagAll">&lt;/</span><span class="MetaTagAll">ul</span><span class="MetaTagAll">&gt;</span></span>
</pre>
<p>To give the user ability to move the menu items around, I added &#8220;move_up&#8221; and &#8220;move_down&#8221; actions to the Page Controller in which I called the &#8220;move_higher&#8221; and &#8220;move_lower&#8221; methods that are mixed in by the acts_as_list.  I realized rather quickly that the position index was getting out of sync and added a reindex method to the Page model to clean up the data along with a scope declaration to the model.  Along the way, I also realized that if I moved a page from one parent node to another parent node, I potentially opened a gap in the position index in the collection of Pages and this again breaks the position index sequencing.  The acts_as_list mix-in expects the position index to always go [0, 1, 2, 3, ...N].  Whenever this is not the case, the move_higher and move_lower methods stop working and the user interface no longer responds correctly.  So this is why we care so much about the position index sequence.  So, to handle scoping of the position index sequence correctly and to handle Pages being moved to another parent, we arrive at this final version of the Page model:
</p>
<pre class="twilight"><span class="line-numbers">   1 </span> <span class="Keyword">class</span> <span class="Entity">Page<span class="EntityInheritedClass"> <span class="EntityInheritedClass">&lt;</span> ActiveRecord::Base</span></span>
<span class="line-numbers">   2 </span>   acts_as_tree <span class="Constant"><span class="Constant">:</span>order</span> =&gt; <span class="Constant"><span class="Constant">:</span>position</span>
<span class="line-numbers">   3 </span>   acts_as_list <span class="Constant"><span class="Constant">:</span>scope</span> =&gt; <span class="Constant"><span class="Constant">:</span>parent_id</span>
<span class="line-numbers">   4 </span>
<span class="line-numbers">   5 </span>   before_save <span class="Constant"><span class="Constant">:</span>keep_position_sane</span>
<span class="line-numbers">   6 </span>
<span class="line-numbers">   7 </span>   <span class="Keyword">def</span> <span class="Entity">self.top_level_pages</span>
<span class="line-numbers">   8 </span>     <span class="Entity">find</span>(<span class="Constant"><span class="Constant">:</span>all</span>, <span class="Constant"><span class="Constant">:</span>conditions</span> =&gt; [<span class="String"><span class="String">&quot;</span>parent_id IS NULL<span class="String">&quot;</span></span>], <span class="Constant"><span class="Constant">:</span>order</span> =&gt; <span class="Constant"><span class="Constant">:</span>position</span>)
<span class="line-numbers">   9 </span>   <span class="Keyword">end</span>
<span class="line-numbers">  10 </span>
<span class="line-numbers">  11 </span>   <span class="Keyword">def</span> <span class="Entity">self.reindex_top_level_pages</span>(<span class="Variable">recurse <span class="Keyword">=</span> <span class="Constant">true</span><span class="Variable">,</span> departing_child <span class="Keyword">=</span> <span class="Constant">nil</span></span>)
<span class="line-numbers">  12 </span>     <span class="Entity">reindex_pages</span>(<span class="Variable">self</span>.<span class="Entity">top_level_pages</span>, recurse, departing_child)
<span class="line-numbers">  13 </span>   <span class="Keyword">end</span>
<span class="line-numbers">  14 </span>
<span class="line-numbers">  15 </span>   <span class="Keyword">def</span> <span class="Entity">reindex_children</span>(<span class="Variable">recurse <span class="Keyword">=</span> <span class="Constant">true</span><span class="Variable">,</span> departing_child <span class="Keyword">=</span> <span class="Constant">nil</span></span>)
<span class="line-numbers">  16 </span>     <span class="Support">Page</span>.<span class="Entity">reindex_pages</span>(children, recurse, departing_child)
<span class="line-numbers">  17 </span>   <span class="Keyword">end</span>
<span class="line-numbers">  18 </span>
<span class="line-numbers">  19 </span>   <span class="Keyword">private</span>
<span class="line-numbers">  20 </span>
<span class="line-numbers">  21 </span> <span class="Comment">  <span class="Comment">#</span> takes a given array of pages and recursively (or not) reindexes</span>
<span class="line-numbers">  22 </span> <span class="Comment">  <span class="Comment">#</span> if departing_child is supplied, it is removed from the array so </span>
<span class="line-numbers">  23 </span> <span class="Comment">  <span class="Comment">#</span> that former siblings are reindexed as though it was already </span>
<span class="line-numbers">  24 </span> <span class="Comment">  <span class="Comment">#</span> removed from the collection.</span>
<span class="line-numbers">  25 </span>   <span class="Keyword">def</span> <span class="Entity">self.reindex_pages</span>(<span class="Variable">pages<span class="Variable">,</span> recurse<span class="Variable">,</span> departing_child</span>)
<span class="line-numbers">  26 </span>     pages.<span class="Entity">select</span>{|<span class="Variable">r</span>| r <span class="Keyword">!=</span> departing_child}.<span class="Entity">each_with_index</span> <span class="Keyword">do </span>|<span class="Variable">page</span>, <span class="Variable">index</span>|
<span class="line-numbers">  27 </span>       page.<span class="Entity">reindex_children</span>(<span class="Constant">true</span>) <span class="Keyword">if</span> recurse
<span class="line-numbers">  28 </span>       page.<span class="Entity">update_attributes</span>(<span class="Constant"><span class="Constant">:</span>position</span> =&gt; index <span class="Keyword">+</span> <span class="Constant">1</span>)
<span class="line-numbers">  29 </span>     <span class="Keyword">end</span>
<span class="line-numbers">  30 </span>     <span class="Constant">true</span>
<span class="line-numbers">  31 </span>   <span class="Keyword">end</span>
<span class="line-numbers">  32 </span>
<span class="line-numbers">  33 </span> <span class="Comment">  <span class="Comment">#</span> When the parent id of a node changes, the acts_as_list gets lost, so </span>
<span class="line-numbers">  34 </span> <span class="Comment">  <span class="Comment">#</span> we need to reindex the affected nodes to keep things sane</span>
<span class="line-numbers">  35 </span>   <span class="Keyword">def</span> <span class="Entity">keep_position_sane</span>
<span class="line-numbers">  36 </span>     <span class="Keyword">return</span> <span class="Keyword">unless</span> <span class="Variable">self</span>.<span class="Entity">parent_id_changed?</span>
<span class="line-numbers">  37 </span>
<span class="line-numbers">  38 </span> <span class="Comment">    <span class="Comment">#</span> reindex the group this page is being removed from</span>
<span class="line-numbers">  39 </span>     <span class="Keyword">if</span> <span class="Variable">self</span>.<span class="Entity">parent_id_was</span>.<span class="Entity">nil?</span> <span class="Keyword">then</span>
<span class="line-numbers">  40 </span>       <span class="Support">Page</span>.<span class="Entity">reindex_top_level_pages</span>(<span class="Constant">false</span>, <span class="Variable">self</span>)
<span class="line-numbers">  41 </span>     <span class="Keyword">else</span>
<span class="line-numbers">  42 </span>       <span class="Support">Page</span>.<span class="Entity">find</span>(<span class="Variable">self</span>.<span class="Entity">parent_id_was</span>).<span class="Entity">reindex_children</span>(<span class="Constant">false</span>, <span class="Variable">self</span>)
<span class="line-numbers">  43 </span>     <span class="Keyword">end</span>
<span class="line-numbers">  44 </span>
<span class="line-numbers">  45 </span> <span class="Comment">    <span class="Comment">#</span> make this page the last sibling of the new parent group of pages</span>
<span class="line-numbers">  46 </span>     last_page <span class="Keyword">=</span> (<span class="Variable">self</span>.<span class="Entity">parent_id</span>.<span class="Entity">nil?</span> <span class="Keyword">?</span> <span class="Support">Page</span>.<span class="Entity">top_level_pages</span>.<span class="Entity">last</span> : <span class="Support">Page</span>.<span class="Entity">find</span>(<span class="Variable">self</span>.<span class="Entity">parent_id</span>).<span class="Entity">children</span>.<span class="Entity">last</span>)
<span class="line-numbers">  47 </span>     <span class="Variable">self</span>.<span class="Entity">position</span> <span class="Keyword">=</span> (last_page.<span class="Entity">nil?</span> <span class="Keyword">?</span> <span class="Constant">1</span> : last_page.<span class="Entity">position</span> <span class="Keyword">+</span> <span class="Constant">1</span>)
<span class="line-numbers">  48 </span>     <span class="Constant">true</span>
<span class="line-numbers">  49 </span>   <span class="Keyword">end</span>
<span class="line-numbers">  50 </span> <span class="Keyword">end</span>
</pre>
<p>Check out the &#8220;keep_position_sane&#8221; callback.  You&#8217;ll see a nifty application of the new Dirty records feature of ActiveRecord (which I believe was released with Rails 2.2) and was covered by Ryan Diagle in his post,<br />
<a href="http://ryandaigle.com/articles/2008/3/31/what-s-new-in-edge-rails-dirty-objects">What&#8217;s New in Edge Rails: Dirty Objects</a>.  In order to detect that the parent node was indeed changing and which parent&#8217;s collection the Page belonged to, I checked the &#8220;parent_id_was&#8221; value.  In this case, I had to handle top level pages being moved into another Page&#8217;s collection as well as A sub-page being promoted to top-level.
</p>
<h2>A Handy Rake Task</h2>
<p>If you&#8217;re wondering why the recursive parameter and otherwise unnecessarily complex procedures, its because I also added a rake task to recursively reindex all pages in the menu hierarchy:</p>
<pre class="twilight"><span class="line-numbers">   1 </span>   namespace <span class="Constant"><span class="Constant">:</span>pages</span> <span class="Keyword">do</span>
<span class="line-numbers">   2 </span>     desc <span class="String"><span class="String">&quot;</span>re-index page positions<span class="String">&quot;</span></span>
<span class="line-numbers">   3 </span>     task <span class="Constant"><span class="Constant">:</span>reindex</span> =&gt; <span class="Constant"><span class="Constant">:</span>environment</span> <span class="Keyword">do</span>
<span class="line-numbers">   4 </span>       <span class="Support">Page</span>.<span class="Entity">reindex_top_level_pages</span>
<span class="line-numbers">   5 </span>     <span class="Keyword">end</span>
<span class="line-numbers">   6 </span>   <span class="Keyword">end</span>
</pre>
<h2>Conclusion</h2>
<p>Rails has really come a long ways since its early days, but its still not without its little &#8220;gotchas&#8221; and sometimes its tough to uncover silent failures (like with the position order clause not being rendered from the acts_as_list plugin) when mix-ins from different plug-ins are utilized.  Hopefully, this article shows you a few new tricks and how to utilize somewhat competing plugins safely within one model.  The new Dirty attributes feature of ActiveRecord definitely made the chore of implementing this functionality much easier and sane than it would&#8217;ve been back in the Rails 1.x days.</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/one-activerecord-model-acting-as-a-list-and-tree/33/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rails vs. Ramaze Performance Comparison</title>
		<link>http://ramblings.gibberishcode.net/archives/rails-vs-ramaze-performance-comparison/31</link>
		<comments>http://ramblings.gibberishcode.net/archives/rails-vs-ramaze-performance-comparison/31#comments</comments>
		<pubDate>Thu, 19 Feb 2009 21:07:24 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Servers]]></category>
		<category><![CDATA[Systems]]></category>
		<category><![CDATA[mri ruby]]></category>
		<category><![CDATA[passenger]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ramaze]]></category>
		<category><![CDATA[ree ruby]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=31</guid>
		<description><![CDATA[One of my biggest concerns of late is that my &#8220;more than just a little trivial&#8221; Rails projects seem to find their way straight into the heavyweight category in no time at all.  While I am quite hopeful that Merb being the 3.0 version of Rails will resolve many of the issues I face [...]]]></description>
			<content:encoded><![CDATA[<p>One of my biggest concerns of late is that my &#8220;more than just a little trivial&#8221; Rails projects seem to find their way straight into the heavyweight category in no time at all.  While I am quite hopeful that Merb being the 3.0 version of Rails will resolve many of the issues I face today, I have no idea when I can truly count on Rails 3.0&#8217;s arrival at the party.  Thus, I have begun looking at other frameworks that are available today.</p>
<h2>Introducing Ramaze</h2>
<p>One I found is <a href="http://www.ramaze.net">Ramaze</a>.  I have to say that Ramaze is a very minimalist approach to a framework that is actually rather enlightening.  For one coming from a Rails&#8217; world, it can feel a bit sparse at first.  But lately, I&#8217;m attributing my lack of comfort to being put on my toes to do some actual real Ruby coding.  Its both a little scary and liberating at the same time.  I have found that I am enjoying Ruby a lot more as a result of my exposure to Ramaze and my thought process is actually radically changing, almost like a Visual Basic programmer going to Borland Delphi might feel.</p>
<p>So I decided to take a hard look at performance metrics between Rails and Ramaze&#8217;s.  At the same time, I wanted to see how MRI 1.8.7 stacked up against Ruby Enterprise Ed. 1.8.6, so I&#8217;m satisfying a dual curiosity herein.  Being quite happy with <a href="http://www.mod_rails.com">Phusion Passenger</a>, I decided to set things up with Apache 2.2.9 + Passenger + Rails/Ramaze.  Since I&#8217;m quite comfortable with ERB and ActiveRecord, I tweaked Ramaze to be a bit more like the familiar Rails world I am familiar with.</p>
<h2>Un-DRYing the Results</h2>
<p>There&#8217;s no point in generating a lot of data and reporting on it if others cannot repeat yourself (CRY!) in replicating the results, so with that in mind, I attempt to refrain from further bad turn of words by busying myself in pushing everything I utilized to <a href="http://github.com/mwlang/benchmarks/tree/master">github.com</a> and documenting the basic <a href="http://ramblings.gibberishcode.net/archives/bootstrap-ubuntu-server-810-intrepid-64-bit/29">bootstrapping of Ubuntu Intrepid</a>.  For these tests in particular, I used two scripts, <a href="http://github.com/mwlang/bootstrap-scripts/blob/af44b371a24a79820c98fcf9cfe014b7a523a24a/ubuntu/intrepid/bootstrap-passenger-std.sh">bootstrap-passenger-std.sh</a> and <a href="http://github.com/mwlang/bootstrap-scripts/blob/af44b371a24a79820c98fcf9cfe014b7a523a24a/ubuntu/intrepid/bootstrap-passenger-ent.sh">bootstrap-passenter-ent.sh</a> to set up four distinct configurations under the Ruby1.8 interpreter.  Ruby 1.9.1. was a bit difficult to set up and there is a <a href="http://github.com/mwlang/bootstrap-scripts/blob/af44b371a24a79820c98fcf9cfe014b7a523a24a/ubuntu/intrepid/bootstrap-passenger-191.sh">bootstrap-passenger-191.sh</a> script that I assembled as I installed things, but I haven&#8217;t run it to ensure it works end-to-end.   </p>
<table style="width: 70%;" border="1">
<thead>
<tr>
<td style="text-align: center;" colspan="5">Apache 2.2.2</td>
</tr>
<tr>
<td style="text-align: center;" colspan="4">Passenger 2.0.6<br/>(gem)</td>
<td style="text-align: center;">Passenger 2.1.0<br/>(source)</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2">Rails 2.2.2</td>
<td style="text-align: center;" colspan="2">Ramaze 2009.01</td>
<td style="text-align: center;" colspan="1">Ramaze 2009.01</td>
</tr>
<tr>
<td style="text-align: center;">MRI Ruby 1.8.7<br/>package</td>
<td style="text-align: center;">Ent. Ruby 1.8.6<br/>source</td>
<td style="text-align: center;">MRI Ruby 1.8.7<br/>package</td>
<td style="text-align: center;">Ent. Ruby 1.8.6<br/>source</td>
<td style="text-align: center;">MRI Ruby 1.9.1<br/>source</td>
</tr>
</thead>
</table>
<p>I utilized this simple call to Apache Bench to run through the four test scenarios:<br />
All data collected was based on 10,000 requests at 100 concurrent users after an initial &#8220;warmup&#8221; of the passenger threads with start of 100 requests at 10 concurrent users.  The following command shows the basic set of parameters issued to Apache Bench:</p>
<pre class="twilight">
ab -n 10000 -c 100 http://demo.u64rails01.local/posts
</pre>
<p><br/></p>
<h2>What was tested?</h2>
<p>In order to compare, as close as possible, apples to apples, I opted to use Active Record and ERubis in Ramaze to set up the same MVC classes in both the Rails and Ramaze project.  ActiveRecord is probably the heaviest weighted ORM that can be utilized with Ramaze.  I tried briefly to get Erubis working with Rails so that the two frameworks&#8217; projects would be more closely aligned than the out-of-box ERB rendering Rails ships with would have afforded.  I did attempt Erubis with Rails, however, I was either doing something very wrong or Erubis under Rails takes a sizable performance hit and I stuck with out-of-box ERB for these tests given its better performance.  As such, Rails uses out of box ERB rendering in these tests.</p>
<p>The <a href="http://github.com/mwlang/benchmarks/tree/0baffeb9cf3eb514912c6de34b64dc2ce25c2e31/2009/02">application itself</a> is ridiculously simple, offering only one model and performing only a read against the database, but hopefully, an effective start towards building this benchmark framework towards more substantive benchmark tests to come.  For now, lets take a look at how Rails and Ramaze stack up to each other and what affect the underlying interpreter can have on performance.  Standard packaged Main Ruby Interpreter (MRI) 1.8.7 consisted of simple package install provided by the Ubuntu Intrepid repositories whle <a href="http://www.rubyenterpriseedition.com/">Ruby Enterprise Edition</a> (REE) 1.8.6, which hails from the same folks bringing you Phusion Passenger, is compiled from source.</p>
<p>As a bonus, I also compiled and clocked metrics for Ramaze on 1.9.1.  This was a tricky environment to get set up and working.  And my initial results made me think I did something horribly wrong with compiling and configuring Ruby 1.9.1.  However, I fully replicated Antonio Cangiano&#8217;s benchmarks in my test environment (see <a href="http://antoniocangiano.com/2008/12/09/the-great-ruby-shootout-december-2008/">The Great Ruby Shootout (December 2008)</a>) and MRI 1.9.1. did indeed crush the other Ruby interpreters in Fibonacci number crunching (amongst others).  The theory tossed around in the Ramaze IRC channel was that 1.9.1. change in implementation of the String class (and it being unicode native now) slows things down a good bit with string processing.</p>
<h2>The Results</h2>
<p>The following table tabulates the results gathered from Apache Bench as well as passenger-memory-stat.  I ran a short run to warm things up (100 requests, 10 concurrently).  And then ran the big bench at 10,000 requests, 100 concurrently, and then ran again and recorded the results.  At around 5,000 requests, I ran passenger-memory-stat to collect the data during mid-processing and then again at the end.  These tests were run on a Ubuntu 64-bit Intrepid 8.10 virtual server that was allocated 1 CPU core and 512MB of RAM.  Swap space was never triggered (0k utilization), and CPU utilization was roughly 85% within the Virtual Machine during the test runs.  The host machine, which is four core CPU, showed one core busy at near 100% with the other three cores pretty much idle.</p>
<table border="0" cellspacing="0" frame="void" rules="none">
<tbody>
<tr>
<td width="138" height="17" align="left"><strong></strong></td>
<td width="86" align="right"><strong>mri_rails</strong></td>
<td width="86" align="right"><strong>ree_rails</strong></td>
<td width="86" align="right"><strong>mri_ramaze</strong></td>
<td width="86" align="right"><strong>ree_ramaze</strong></td>
<td width="86" align="right"><strong>191_ramaze</strong></td>
<td width="86" align="left"><strong>units</strong></td>
</tr>
<tr>
<td height="16" align="right"><strong>Time taken for tests</strong></td>
<td align="right">489.24</td>
<td align="right">379.76</td>
<td align="right">397.43</td>
<td align="right">287.5</td>
<td align="right">351.95</td>
<td align="left"><em>seconds</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Complete requests</strong></td>
<td align="right">10000</td>
<td align="right">10000</td>
<td align="right">10000</td>
<td align="right">10000</td>
<td align="right">10000</td>
<td align="left"><em></em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Failed requests</strong></td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="left"><em></em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Write errors</strong></td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="right">0</td>
<td align="left"><em></em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Total transferred</strong></td>
<td align="right">36670146</td>
<td align="right">36668229</td>
<td align="right">18130000</td>
<td align="right">18130000</td>
<td align="right">18460000</td>
<td align="left"><em>bytes</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>HTML transferred</strong></td>
<td align="right">30500000</td>
<td align="right">30500000</td>
<td align="right">14480000</td>
<td align="right">14480000</td>
<td align="right">14680000</td>
<td align="left"><em>bytes</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Requests per second</strong></td>
<td align="right">20.44</td>
<td align="right">26.33</td>
<td align="right">25.19</td>
<td align="right">34.78</td>
<td align="right">28.41</td>
<td align="left"><em>[#/sec] (mean)</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Time per request</strong></td>
<td align="right">4892.42</td>
<td align="right">3797.56</td>
<td align="right">3970.43</td>
<td align="right">2875.03</td>
<td align="right">3519.49</td>
<td align="left"><em>[ms] (mean)</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Time per request</strong></td>
<td align="right">48.92</td>
<td align="right">37.98</td>
<td align="right">39.7</td>
<td align="right">28.75</td>
<td align="right">35.2</td>
<td align="left"><em>[ms] (mean, across all concurrent requests)</em></td>
</tr>
<tr>
<td height="17" align="right"><strong>Transfer rate</strong></td>
<td align="right">73.19</td>
<td align="right">94.29</td>
<td align="right">44.59</td>
<td align="right">61.58</td>
<td align="right">51.22</td>
<td align="left"><em>[Kbytes/sec] received</em></td>
</tr>
<tr>
<td height="16" align="right"><strong>Total private dirty RSS</strong></td>
<td align="right">323.73</td>
<td align="right">180.89</td>
<td align="right">221.11</td>
<td align="right">221.51</td>
<td align="right">210.73</td>
<td align="left"><em>MB</em></td>
</tr>
</tbody>
</table>
<h2>Throughput</h2>
<p>Everybody likes to <a href="http://www.therailsway.com/2009/1/6/requests-per-second">talk about throughput</a>, so lets start there.  As you can see, MRI and Rails was the worst performer while REE and Ramaze clocked in with the highest throughput in terms of requests per second.  Be forewarned that throughput doesn&#8217;t necessarily equate to user response times.  This metric simply tells you the load at which your system is capable of delivering content at.</p>
<p><img src='http://github.com/mwlang/benchmarks/raw/0baffeb9cf3eb514912c6de34b64dc2ce25c2e31/2009/02/throughput.png' alt='throughput' class='alignnone' /></p>
<h2>Time per Request</h2>
<p>Time per request helps you see about how long an average user request takes for a given load.  As you can see here, with times in the milliseconds, we&#8217;re talking about numbers that will easily be swallowed in the noise of network latency.  Even so, its not hard to be impressed by what the REE folks managed to pull out of their hat with regards to performance on the main ruby interpreter.</p>
<p><img src='http://github.com/mwlang/benchmarks/raw/0baffeb9cf3eb514912c6de34b64dc2ce25c2e31/2009/02/time_per_request.png' alt='Time per Request' class='alignnone' /></p>
<h2>Memory Consumption</h2>
<p>This final graph shows the sizeable dent REE makes in memory consumption for running a Rails application.  Ramaze is already pretty efficient and lean and REE made practically no dent in memory consumption here.  However, REE&#8217;s use of the faster memory allocation routines does have a good effect on boosting Ramaze&#8217;s performance (as seen in above graphs), so there&#8217;s still lots to be gained by running Ramaze with REE.</p>
<p><img src='http://github.com/mwlang/benchmarks/raw/0baffeb9cf3eb514912c6de34b64dc2ce25c2e31/2009/02/memory_usage.png' alt='Memory Usage' class='alignnone' /></p>
<h2>Conclusion</h2>
<p>For me, I have two very, very viable solutions before me.  Rails on Enterprise Ruby or Ramaze on Ruby 1.8.7.  The final choice comes down to how much I can bet on my own Ruby prowess.   Ramaze is a very lean framework that gets you going, but doesn&#8217;t get you a nine inch pillowed mattress topping for your bed like Rails does.  Because Ramaze is so lean, I realize I will have to really take my Ruby skills to the next level and truly understand the ruby code that other developers produce.  Unlike Rails, where there&#8217;s gobs and gobs of plugins and blogs on the topic that make pretty much any need simple to satisfy and get working, I have to know my Ruby to get a solution implemented on Ramaze.  Not a bad thing, but certainly a more challenging and potentially rewarding path to take.</p>
<p>Aside from my personal concerns about making the jump, looking at these results, it is good to see that I have options and how easy it is to get a good deployment up and working compared to 3 or 4 years ago when I was banging my head against the wall for days on end to keep lighttpd + fcgi processes up and running around the clock.  In these tests, it was very, very satisfying that not one single crash or dropped request was recorded in all the benchmarking I performed.  Mongrel came along and made a big impact, but you still needed something like monit to watch Mongrel and restart it on ocvasion.  Now Passenger has come along and made yet another big impact in the ease of deploying Rails, Ramaze, or any of the other ruby-based frameworks.  We&#8217;ve come a long ways, indeed.  I, for one, certainly look forward to what the Ruby sphere will bring us over the next couple of years.</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/rails-vs-ramaze-performance-comparison/31/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Install Passenger on Ubuntu Intrepid 8.10</title>
		<link>http://ramblings.gibberishcode.net/archives/install-passenger-on-ubuntu-intrepid-810/30</link>
		<comments>http://ramblings.gibberishcode.net/archives/install-passenger-on-ubuntu-intrepid-810/30#comments</comments>
		<pubDate>Sat, 14 Feb 2009 15:03:16 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Configuration]]></category>
		<category><![CDATA[Setups]]></category>
		<category><![CDATA[Bootsrap]]></category>
		<category><![CDATA[enterprise ruby]]></category>
		<category><![CDATA[passenger]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=30</guid>
		<description><![CDATA[Phusion Passenger is one of the easiest Rails and Rack environments to set up and configure with Apache2.  With my bootstrapping scripts, the ability to establish a Virtual machine image and have a working Rails application on it in minutes is a reality.  Here is how I do it.
Bootstrap Passenger
Begin by Bootstrapping Ubuntu. [...]]]></description>
			<content:encoded><![CDATA[<p>Phusion Passenger is one of the easiest Rails and Rack environments to set up and configure with Apache2.  With my bootstrapping scripts, the ability to establish a Virtual machine image and have a working Rails application on it in minutes is a reality.  Here is how I do it.</p>
<h2>Bootstrap Passenger</h2>
<p>Begin by <a href="http://ramblings.gibberishcode.net/archives/bootstrap-ubuntu-server-810-intrepid-64-bit/29">Bootstrapping Ubuntu</a>.  </p>
<p>The next step is to run the script for installing Passenger.  The standard passenger install against the standard Ruby 1.8.7 package managed libraries are installed with the following:</p>
<p>(assuming you&#8217;ve downloaded the bootstrap scripts and are in the ~/bootstrap-scripts/ubuntu/intrepid folder already, execute the following script:</p>
<pre class="twilight">
./bootstrap-passenger-std.sh
</pre>
<p>Passenger&#8217;s install prompts you two or three times.  Simply press <ENTER> and ignore the additional configuration instructions (the bootstrap script carries out these instructions for you).</p>
<h2>Set up a demo Rails project</h2>
<pre class="twilight">
cd /var/www
sudo mkdir rails
cd rails
sudo rails demo
cd ..
chown -R www-data:www-data rails
</pre>
<h2>Set up a Virtual Host Entry</h2>
<p>Apache2 on Ubuntu makes it fairly straightforward to set up a provider for the Rails application, simply create the following in /etc/apache2/sites-available/demo:</p>
<pre class="twilight">
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagInline">VirtualHost</span> <span class="MetaTagAll">*:80</span><span class="MetaTagAll">&gt;</span></span>
  <span class="SupportConstant">ServerName</span> localhost
  <span class="SupportConstant">DocumentRoot</span> /var/www/rails/demo/public
<span class="MetaTagAll"><span class="MetaTagAll">&lt;/</span><span class="MetaTagInline">VirtualHost</span><span class="MetaTagAll">&gt;</span></span>
</pre>
<p>And then enable the site:</p>
<pre class="twilight">
sudo a2ensite demo
sudo /etc/init.d/apache2 reload
</pre>
<h2>End Result</h2>
<p>If all goes well, you should then be able to browse to http://localhost and see the rails greet page.  Your system will look something like this:</p>
<pre class="twilight"><span class="String"><span class="MetaTagInline">OS<span class="MetaTagInline">:</span></span> <span class="String">2.6.27-7-server x86_64 GNU/Linux</span></span>
<span class="String"><span class="MetaTagInline">Apache<span class="MetaTagInline">:</span></span> <span class="String">Server version: Apache/2.2.9 (Ubuntu)</span></span>
<span class="String"><span class="MetaTagInline">Passenger<span class="MetaTagInline">:</span></span> <span class="String">2.0.6</span></span>
<span class="String"><span class="MetaTagInline">Ruby<span class="MetaTagInline">:</span></span> <span class="String">ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]</span></span>
<span class="String"><span class="MetaTagInline">Rubygems<span class="MetaTagInline">:</span></span> <span class="String">1.3.1</span></span>
<span class="MetaTagAll"><span class="MetaTagInline">gems</span><span class="MetaTagAll">:</span></span>
 <span class="String"><span class="String">-</span> <span class="String">actionmailer (2.2.2)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">actionpack (2.2.2)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">activerecord (2.2.2)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">activeresource (2.2.2)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">activesupport (2.2.2)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">fastthread (1.0.1)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">passenger (2.0.6)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">plist (3.0.0)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">rack (0.9.1)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">rails (2.2.2)</span></span>
 <span class="String"><span class="String">-</span> <span class="String">rake (0.8.3)</span></span>
</pre>
<h2>Where to go from here?</h2>
<p>After you have Passenger working, you may be interested in trying it with the so-called <a href="http://www.rubyenterpriseedition.com/">Enterprise version of Ruby</a> which claims 33% memory footprint reduction overall for a typical Rails application.  If so, you can flip over to the Enterprise version by running the following bootstrap script:</p>
<pre class="twilight">
./bootstrap-passenger-ent.sh
</pre>
<p>This script will download and compile Enterprise Ruby and install Passenger against this version of Ruby, disabling the standard passenger modules in Apache (if they&#8217;re installed as per above) in the process.  Does it make any real difference?  We shall see!  I am benchmarking performance of both standard and enterprise and will be reporting on my findings soon. </p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/install-passenger-on-ubuntu-intrepid-810/30/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Bootstrap Ubuntu Server 8.10 Intrepid 64-bit</title>
		<link>http://ramblings.gibberishcode.net/archives/bootstrap-ubuntu-server-810-intrepid-64-bit/29</link>
		<comments>http://ramblings.gibberishcode.net/archives/bootstrap-ubuntu-server-810-intrepid-64-bit/29#comments</comments>
		<pubDate>Sat, 14 Feb 2009 12:46:05 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Bootsrap]]></category>
		<category><![CDATA[Setups]]></category>
		<category><![CDATA[intrepid]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=29</guid>
		<description><![CDATA[This post covers getting the basic Ubuntu Server 8.10 Intrepid up and going and ready for further configuration (such as Ruby on Rails, or MySQL Server, or Bind 9, etc.).

Ubuntu Site:  http://www.ubuntu.com
ISO Image:  ubuntu-8.10-server-amd64.iso

If you&#8217;re new to my bootstrapping series, please see Bootstrapping my blog for some background information.
Install the OS
No need to [...]]]></description>
			<content:encoded><![CDATA[<p>This post covers getting the basic Ubuntu Server 8.10 Intrepid up and going and ready for further configuration (such as Ruby on Rails, or MySQL Server, or Bind 9, etc.).</p>
<ul>
<li>Ubuntu Site:  http://www.ubuntu.com</li>
<li>ISO Image:  ubuntu-8.10-server-amd64.iso</li>
</ul>
<p>If you&#8217;re new to my bootstrapping series, please see <a href="http://ramblings.gibberishcode.net/archives/bootstrapping-my-blogs/28">Bootstrapping my blog</a> for some background information.</p>
<h2>Install the OS</h2>
<p>No need to beat a dead horse to death.  If you need instructions for installing Ubuntu, then please follow Ubuntu&#8217;s most excellent guides for installing Ubuntu Server.  For my purposes, I install everything to default choices, only choosing to install OpenSSH Server (and no others).  Everything else is simply supplying the simple information requested at each prompt in order to get me to the bootstrapping stage most directly.</p>
<h2>Bootstrap!</h2>
<p>Once the OS is installed and boots for the first time, log in and run the following:</p>
<pre class="twilight">
<span class="line-numbers">   1 </span> sudo apt-get install git-core
<span class="line-numbers">   2 </span> git clone git://github.com/mwlang/bootstrap-scripts.git
<span class="line-numbers">   3 </span> cd bootstrap-scripts/ubuntu/intrepid
<span class="line-numbers">   4 </span> sh bootstrap.sh
</pre>
<p>This will install the prerequisite git commands and check out the <a>bootstrap-script repository</a> and then run through upgrading the Ubuntu server and install the build-essentials.</p>
<p>That&#8217;s it!  You can now return to the article that brought you here.</p>
<h2>Nice, but what is this all about?</h2>
<p>Bootstrapping is just that.  Getting the bare minimum components installed so that you can proceed to do more complicated tasks.  Rather than writing things over and over, I scripted them and checked them into git repository and then refer to them, hopefully much to your gain (as well as my sanity).</p>
<p>Depending on where you want to go with your server instance, you can run any of the other bootstrap scripts in the folder.  For example, to install Ruby 1.8.7 from packages (not source) and install Apache2 and Passenger and Rails, run the following script:</p>
<pre class="twilight">
<span class="line-numbers">   1 </span> sh bootstrap-passenger-std.sh
</pre>
<p>You&#8217;ll need to hit the  key a couple of times when passenger&#8217;s installer runs, but otherwise, should run start-to-finish, installing everything without any further action on your part to get a fully functioning Apache2 + Passenger + MRI Ruby 1.8.7 + Rails stack installed.</p>
<p>Be careful, though.  I didn&#8217;t put any dependency checks around these scripts.  Its fairly safe to run any of the bootstrap-XXX.sh scripts without running anything else (these scripts do call the main bootstrap.sh script listed above).  But several scripts, such as rails.sh do indeed have prerequisites and you <em>will</em> have issues running these out of order.  Also, bootstrapping is targeting specific outcomes.  Don&#8217;t expect to run one bootstrap-XXX.sh script and then run another.  They are very likely to collide with undesirable outcomes.</p>
<p>Documentation will be fairly sparse in the git repository, with the intent to spell things out in the accompanying blog articles.</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/bootstrap-ubuntu-server-810-intrepid-64-bit/29/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Bootstrapping my Blogs</title>
		<link>http://ramblings.gibberishcode.net/archives/bootstrapping-my-blogs/28</link>
		<comments>http://ramblings.gibberishcode.net/archives/bootstrapping-my-blogs/28#comments</comments>
		<pubDate>Sat, 14 Feb 2009 12:42:14 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[Setups]]></category>
		<category><![CDATA[bootstrapping]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[dry]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=28</guid>
		<description><![CDATA[Ever notice that blogs that are tutorial in nature spend a lot of time providing instructions on installing things, but a whole lot depends on what the author already has installed that he&#8217;s forgotten to tell you about?  Or perhaps the author simply left out the details necessary for you to fully replicate his [...]]]></description>
			<content:encoded><![CDATA[<p>Ever notice that blogs that are tutorial in nature spend a lot of time providing instructions on installing things, but a whole lot depends on what the author already has installed that he&#8217;s forgotten to tell you about?  Or perhaps the author simply left out the details necessary for you to fully replicate his environment?</p>
<p>For me personally, I get downright tired of writing the same thing over and over.  Like my code, I want my blogging efforts to be efficient and DRY.  Since I work extensively with virtual machines to maintain an orderly environment in a known state, I figured it makes just as much sense to document by establishing a git repository that contains all my bootstrap scripts and configuration scripts needed to get things installed.  Whether you use bare metal or virtual machines, you are now empowered to fully recreate the environments I discuss in my posts.  </p>
<p>Now, rather than constantly regurgitating basic setups in a sort of preamble to the topic I wish to discuss, I will simply link to my Setup specific articles to get you going and then dive straight into the topic at hand.</p>
<p>It all begins with you establishing your basic out-of-box instance, installing git, then pulling the bootstrap scripts down from my <a href="http://github.com/mwlang/bootstrap-scripts/tree/master">bootstrap-scripts repository</a>.</p>
<p>The three environments I intend to discuss most (at this time) is Ubuntu 8.10 (Intrepid), CentOS 5.2, and Leopard OS X.  I will not give each of these equal coverage with respect to bootstrapping as I am presently only bootstrapping Ubuntu on a regular basis (and I haven&#8217;t reinstalled Leopard in ages).  However, when I do bring up any new environments, I will make a full effort to fully document bootstrapping that environment and push to git.  For now, you can expect Ubuntu Intrepid to be my most well-documented environment.</p>
<p>Please keep in mind:  These are very simple scripts, highly targeted at the environment they&#8217;re written for.  They&#8217;re not meant to do the job one might employ capistrano or puppet for.  I know what I want my target environment to be and I aim straight at it with these scripts.  Also, the repository will be organized atypically for preserving history rather than for merging commits into a well-maintained master branch.</p>
<p>All articles dealing with operating system and package installs will be filed under the Setups category and, if appropriate, the pertinent sub-category.</p>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/bootstrapping-my-blogs/28/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Ultraviolet gives new life to Syntax highlighting</title>
		<link>http://ramblings.gibberishcode.net/archives/ultraviolet-gives-new-life-to-syntax-highlighting/27</link>
		<comments>http://ramblings.gibberishcode.net/archives/ultraviolet-gives-new-life-to-syntax-highlighting/27#comments</comments>
		<pubDate>Thu, 05 Feb 2009 08:30:44 +0000</pubDate>
		<dc:creator>Michael</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[mama]]></category>
		<category><![CDATA[oniguruma]]></category>
		<category><![CDATA[textmate]]></category>
		<category><![CDATA[textpow]]></category>
		<category><![CDATA[ultraviolet]]></category>

		<guid isPermaLink="false">http://ramblings.gibberishcode.net/?p=27</guid>
		<description><![CDATA[I am tired of Google&#8217;s Syntax highlighter.  If I accidentally switch to Visual mode in the editor and do some edits and switch back to HTML mode, then I lose the class=&#8221;code&#8221; moniker and I have to go through the entire post and fix.
Another drawback I have with Google Syntax highlighter is that the page [...]]]></description>
			<content:encoded><![CDATA[<p>I am tired of Google&#8217;s Syntax highlighter.  If I accidentally switch to Visual mode in the editor and do some edits and switch back to HTML mode, then I lose the class=&#8221;code&#8221; moniker and I have to go through the entire post and fix.</p>
<p>Another drawback I have with Google Syntax highlighter is that the page renders without highlighting and then all the code listings change to syntax highlighted text a few moments later.</p>
<p>Introducing <a href="http://ultraviolet.rubyforge.org/index.xhtml" target="_blank">Ultraviolet</a>, a syntax highlighter supporting over 50 syntaxes and any <a href="http://macromates.com/" target="_blank">TextMate</a> theme.  Ultraviolet is based on <a href="http://www.geocities.jp/kosako3/oniguruma/" target="_blank">Oniguruma</a>, a regular expression library that comes standard with Ruby 1.9.  Ultraviolet requires a little more effort for me to cut and paste, but I am definitely much happier with the end results and Wordpress no longer mungs my posts.  Only real complaint I have is that I can&#8217;t read the posted code inline in HTML format (but I can read it in Visual mode!).</p>
<h2>Ruby Script</h2>
<pre class="twilight"><span class="line-numbers">   1 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>rubygems<span class="String">'</span></span>
<span class="line-numbers">   2 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>ramaze<span class="String">'</span></span>
<span class="line-numbers">   3 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>maruku<span class="String">'</span></span>
<span class="line-numbers">   4 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>sequel<span class="String">'</span></span>
<span class="line-numbers">   5 </span>
<span class="line-numbers">   6 </span> <span class="Variable"><span class="Variable">$</span>LOAD_PATH</span>.<span class="Entity">unshift</span>(__DIR__)
<span class="line-numbers">   7 </span>
<span class="line-numbers">   8 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>env<span class="String">'</span></span>
<span class="line-numbers">   9 </span>
<span class="line-numbers">  10 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>vendor/flickr<span class="String">'</span></span>
<span class="line-numbers">  11 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>vendor/haml_maruku_filter<span class="String">'</span></span>
<span class="line-numbers">  12 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>vendor/image_science_cropped_resize<span class="String">'</span></span>
<span class="line-numbers">  13 </span>
<span class="line-numbers">  14 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>model/init<span class="String">'</span></span>
<span class="line-numbers">  15 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>controller/init<span class="String">'</span></span>
<span class="line-numbers">  16 </span>
<span class="line-numbers">  17 </span> <span class="Keyword">require</span> <span class="String"><span class="String">'</span>db/init<span class="String">'</span></span> <span class="Keyword">if</span> <span class="Variable">SOCIAR</span>.<span class="Entity">mode</span> <span class="Keyword">==</span> <span class="Constant"><span class="Constant">:</span>dev</span>
<span class="line-numbers">  18 </span>
<span class="line-numbers">  19 </span> r <span class="Keyword">=</span> <span class="Variable">SOCIAR</span>.<span class="Entity">ramaze</span>
<span class="line-numbers">  20 </span>
<span class="line-numbers">  21 </span> <span class="Comment"><span class="Comment">=begin</span></span>
<span class="line-numbers">  22 </span> <span class="Comment">if r.gzip</span>
<span class="line-numbers">  23 </span> <span class="Comment">  require 'ramaze/contrib/gzip_filter'</span>
<span class="line-numbers">  24 </span> <span class="Comment">  gzip = Ramaze::Filter::Gzip</span>
<span class="line-numbers">  25 </span> <span class="Comment">  gzip.trait :threshold =&gt; 1</span>
<span class="line-numbers">  26 </span> <span class="Comment">  Ramaze::Dispatcher::Action::FILTER &lt;&lt; gzip</span>
<span class="line-numbers">  27 </span> <span class="Comment"><span class="Comment">=end</span></span>
<span class="line-numbers">  28 </span>
<span class="line-numbers">  29 </span> <span class="Support">Ramaze</span>.<span class="Entity">start</span> <span class="Constant"><span class="Constant">:</span>adapter</span> =&gt; r.<span class="Entity">adapter</span>,
<span class="line-numbers">  30 </span>              <span class="Constant"><span class="Constant">:</span>host</span> =&gt; r.<span class="Entity">host</span>,
<span class="line-numbers">  31 </span>              <span class="Constant"><span class="Constant">:</span>port</span> =&gt; r.<span class="Entity">port</span>,
<span class="line-numbers">  32 </span>              <span class="Constant"><span class="Constant">:</span>boring</span> =&gt; r.<span class="Entity">boring</span></pre>
<h2>Shell Script</h2>
<pre class="twilight"><span class="line-numbers">   1 </span> <span class="Comment"><span class="Comment">#</span>!/bin/sh</span>
<span class="line-numbers">   2 </span>
<span class="line-numbers">   3 </span> <span class="Comment"><span class="Comment">#</span>## BEGIN INIT INFO</span>
<span class="line-numbers">   4 </span> <span class="Comment"><span class="Comment">#</span> Provides:          lm-sensors</span>
<span class="line-numbers">   5 </span> <span class="Comment"><span class="Comment">#</span> Required-Start:    $local_fs</span>
<span class="line-numbers">   6 </span> <span class="Comment"><span class="Comment">#</span> Required-Stop:</span>
<span class="line-numbers">   7 </span> <span class="Comment"><span class="Comment">#</span> Default-Start:     S</span>
<span class="line-numbers">   8 </span> <span class="Comment"><span class="Comment">#</span> Default-Stop:</span>
<span class="line-numbers">   9 </span> <span class="Comment"><span class="Comment">#</span> Short-Description: lm-sensors</span>
<span class="line-numbers">  10 </span> <span class="Comment"><span class="Comment">#</span> Description:       hardware health monitoring</span>
<span class="line-numbers">  11 </span> <span class="Comment"><span class="Comment">#</span>## END INIT INFO</span>
<span class="line-numbers">  12 </span>
<span class="line-numbers">  13 </span> . /lib/lsb/init-functions
<span class="line-numbers">  14 </span>
<span class="line-numbers">  15 </span> [ -f /etc/default/rcS ] <span class="Keyword">&amp;&amp;</span> . /etc/default/rcS
<span class="line-numbers">  16 </span> PATH=/bin:/usr/bin:/sbin:/usr/sbin
<span class="line-numbers">  17 </span> PROGRAM=/usr/bin/sensors
<span class="line-numbers">  18 </span>
<span class="line-numbers">  19 </span> test -x <span class="Variable"><span class="Variable">$</span>PROGRAM</span> <span class="Keyword">||</span> exit 0
<span class="line-numbers">  20 </span>
<span class="line-numbers">  21 </span> <span class="Keyword">case</span> <span class="String"><span class="String">&quot;</span><span class="Variable"><span class="Variable">$</span>1</span><span class="String">&quot;</span></span> <span class="Keyword">in</span>
<span class="line-numbers">  22 </span>   start)
<span class="line-numbers">  23 </span> 	log_begin_msg <span class="String"><span class="String">&quot;</span>Setting sensors limits<span class="String">&quot;</span></span>
<span class="line-numbers">  24 </span> 	<span class="Keyword">if</span> [ <span class="String"><span class="String">&quot;</span><span class="Variable"><span class="Variable">$</span>VERBOSE</span><span class="String">&quot;</span></span> = <span class="String"><span class="String">&quot;</span>no<span class="String">&quot;</span></span> ]<span class="Keyword">;</span> <span class="Keyword">then</span>
<span class="line-numbers">  25 </span> 		/usr/bin/sensors -s <span class="Keyword">1&gt;</span> /dev/null <span class="Keyword">2&gt;</span> /dev/null
<span class="line-numbers">  26 </span> 		/usr/bin/sensors <span class="Keyword">1&gt;</span> /dev/null <span class="Keyword">2&gt;</span> /dev/null
<span class="line-numbers">  27 </span> 	<span class="Keyword">else</span>
<span class="line-numbers">  28 </span> 		/usr/bin/sensors -s
<span class="line-numbers">  29 </span> 		/usr/bin/sensors <span class="Keyword">&gt;</span> /dev/null
<span class="line-numbers">  30 </span> 	<span class="Keyword">fi</span>
<span class="line-numbers">  31 </span> 	log_end_msg <span class="Variable"><span class="Variable">$</span>?</span>
<span class="line-numbers">  32 </span> 	<span class="Keyword">;</span><span class="Keyword">;</span>
<span class="line-numbers">  33 </span>   stop)
<span class="line-numbers">  34 </span> 	<span class="Keyword">;</span><span class="Keyword">;</span>
<span class="line-numbers">  35 </span>   force-reload<span class="Keyword">|</span>restart)
<span class="line-numbers">  36 </span> 	<span class="Variable"><span class="Variable">$</span>0</span> start
<span class="line-numbers">  37 </span> 	<span class="Keyword">;</span><span class="Keyword">;</span>
<span class="line-numbers">  38 </span>   *)
<span class="line-numbers">  39 </span> 	log_success_msg <span class="String"><span class="String">&quot;</span>Usage: /etc/init.d/sensors {start|stop|restart|force-reload}<span class="String">&quot;</span></span>
<span class="line-numbers">  40 </span> 	exit 1
<span class="line-numbers">  41 </span> <span class="Keyword">esac</span>
<span class="line-numbers">  42 </span>
<span class="line-numbers">  43 </span> exit 0
</pre>
<h2>Apache Config</h2>
<pre class="twilight"><span class="line-numbers">   1 </span> <span class="MarkupTag"><span class="MarkupTag">&lt;</span><span class="MarkupNameOfTag">VirtualHost</span> <span class="MarkupTag">*:80</span><span class="MarkupTag">&gt;</span></span>
<span class="line-numbers">   2 </span> 	<span class="LibraryConstant">ServerName</span> www.stinger.local
<span class="line-numbers">   3 </span> 	<span class="LibraryConstant">ServerAdmin</span> webmaster@localhost
<span class="line-numbers">   4 </span>
<span class="line-numbers">   5 </span> 	<span class="LibraryConstant">DocumentRoot</span> /var/www/
<span class="line-numbers">   6 </span> 	<span class="MarkupTag"><span class="MarkupTag">&lt;</span><span class="MarkupNameOfTag">Directory</span> <span class="MarkupTag">/&gt;</span></span>
<span class="line-numbers">   7 </span> 		<span class="LibraryConstant">Options</span> FollowSymLinks
<span class="line-numbers">   8 </span> 		<span class="LibraryConstant">AllowOverride</span> None
<span class="line-numbers">   9 </span> 	<span class="MarkupTag"><span class="MarkupTag">&lt;/</span><span class="MarkupNameOfTag">Directory</span><span class="MarkupTag">&gt;</span></span>
<span class="line-numbers">  10 </span> 	<span class="MarkupTag"><span class="MarkupTag">&lt;</span><span class="MarkupNameOfTag">Directory</span> /var/www<span class="MarkupTag">/&gt;</span></span>
<span class="line-numbers">  11 </span> 		<span class="LibraryConstant">Options</span> Indexes FollowSymLinks MultiViews
<span class="line-numbers">  12 </span> 		<span class="LibraryConstant">AllowOverride</span> None
<span class="line-numbers">  13 </span> 		<span class="LibraryConstant">Order</span> allow,deny
<span class="line-numbers">  14 </span> 		allow from all
<span class="line-numbers">  15 </span> 	<span class="MarkupTag"><span class="MarkupTag">&lt;/</span><span class="MarkupNameOfTag">Directory</span><span class="MarkupTag">&gt;</span></span>
<span class="line-numbers">  16 </span>
<span class="line-numbers">  17 </span> 	<span class="LibraryConstant">ScriptAlias</span> /cgi-bin/ /usr/lib/cgi-bin/
<span class="line-numbers">  18 </span> 	<span class="MarkupTag"><span class="MarkupTag">&lt;</span><span class="MarkupNameOfTag">Directory</span> &quot;/usr/lib/cgi-bin&quot;<span class="MarkupTag">&gt;</span></span>
<span class="line-numbers">  19 </span> 		<span class="LibraryConstant">AllowOverride</span> None
<span class="line-numbers">  20 </span> 		<span class="LibraryConstant">Options</span> +ExecCGI -MultiViews +SymLinksIfOwnerMatch
<span class="line-numbers">  21 </span> 		<span class="LibraryConstant">Order</span> allow,deny
<span class="line-numbers">  22 </span> 		<span class="LibraryConstant">Allow</span> from all
<span class="line-numbers">  23 </span> 	<span class="MarkupTag"><span class="MarkupTag">&lt;/</span><span class="MarkupNameOfTag">Directory</span><span class="MarkupTag">&gt;</span></span>
<span class="line-numbers">  24 </span>
<span class="line-numbers">  25 </span> 	<span class="LibraryConstant">ErrorLog</span> /var/log/apache2/error.log
<span class="line-numbers">  26 </span>
<span class="line-numbers">  27 </span> 	<span class="Comment"><span class="Comment">#</span> Possible values include: debug, info, notice, warn, error, crit,</span>
<span class="line-numbers">  28 </span> 	<span class="Comment"><span class="Comment">#</span> alert, emerg.</span>
<span class="line-numbers">  29 </span> 	<span class="LibraryConstant">LogLevel</span> warn
<span class="line-numbers">  30 </span>
<span class="line-numbers">  31 </span> 	<span class="LibraryConstant">CustomLog</span> /var/log/apache2/access.log combined
<span class="line-numbers">  32 </span>
<span class="line-numbers">  33 </span>     <span class="LibraryConstant">Alias</span> /doc/ <span class="String"><span class="String">&quot;</span>/usr/share/doc/<span class="String">&quot;</span></span>
<span class="line-numbers">  34 </span>     <span class="MarkupTag"><span class="MarkupTag">&lt;</span><span class="MarkupNameOfTag">Directory</span> <span class="MarkupTag">&quot;/usr/share/doc/&quot;</span><span class="MarkupTag">&gt;</span></span>
<span class="line-numbers">  35 </span>         <span class="LibraryConstant">Options</span> Indexes MultiViews FollowSymLinks
<span class="line-numbers">  36 </span>         <span class="LibraryConstant">AllowOverride</span> None
<span class="line-numbers">  37 </span>         <span class="LibraryConstant">Order</span> deny,allow
<span class="line-numbers">  38 </span>         <span class="LibraryConstant">Deny</span> from all
<span class="line-numbers">  39 </span>         <span class="LibraryConstant">Allow</span> from 127.0.0.0/255.0.0.0 ::1/128
<span class="line-numbers">  40 </span>     <span class="MarkupTag"><span class="MarkupTag">&lt;/</span><span class="MarkupNameOfTag">Directory</span><span class="MarkupTag">&gt;</span></span>
<span class="line-numbers">  41 </span>
<span class="line-numbers">  42 </span> <span class="MarkupTag"><span class="MarkupTag">&lt;/</span><span class="MarkupNameOfTag">VirtualHost</span><span class="MarkupTag">&gt;</span></span>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ramblings.gibberishcode.net/archives/ultraviolet-gives-new-life-to-syntax-highlighting/27/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
