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

<channel>
	<title>Sketchpad - Dan Sketcher's personal blog &#187; Uncategorized</title>
	<atom:link href="http://www.dansketcher.com/category/uncategorized/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.dansketcher.com</link>
	<description>Dan Sketcher on Rails, computers, and other junk</description>
	<lastBuildDate>Wed, 17 Mar 2010 05:42:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Getting Phusion Passenger to run under SELinux on Centos 5.4</title>
		<link>http://www.dansketcher.com/2010/02/25/getting-phusion-passenger-to-run-under-selinux-on-centos-5-4/</link>
		<comments>http://www.dansketcher.com/2010/02/25/getting-phusion-passenger-to-run-under-selinux-on-centos-5-4/#comments</comments>
		<pubDate>Thu, 25 Feb 2010 07:37:14 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/?p=125</guid>
		<description><![CDATA[Edit 2010-03-17: Harder than it first seemed! That permission only allows Apache to load Passenger. See here for the full requirements
I&#8217;ve just started playing with SELinux on Centos, and while the idea is great, it&#8217;s not exactly what I&#8217;m used to. Take for example adding Phusion Passenger to Apache. When I first restarted, I got [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Edit 2010-03-17:</strong> Harder than it first seemed! That permission only allows Apache to load Passenger. <a href="http://stackoverflow.com/questions/2054554/passenger-mod-rails-fails-to-initialize-in-fedora-12-when-starting-apache/2429590#2429590" onClick="javascript:urchinTracker ('/outbound/article/stackoverflow.com');">See here</a> for the full requirements</p>
<p>I&#8217;ve just started playing with SELinux on Centos, and while the idea is great, it&#8217;s not exactly what I&#8217;m used to. Take for example adding Phusion Passenger to Apache. When I first restarted, I got this</p>
<pre>
[root@localhost modules]# /etc/init.d/httpd restart
Stopping httpd:                                            [FAILED]
Starting httpd: httpd: Syntax error on line 210 of /etc/httpd/conf/httpd.conf: Syntax error on line 1 of /etc/httpd/conf.d/passenger.conf: Cannot load /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.9/ext/apache2/mod_passenger.so into server: /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.9/ext/apache2/mod_passenger.so: failed to map segment from shared object: Permission denied
                                                           [FAILED]
</pre>
<p>Huh?</p>
<p>So it turns out after reading through some google results and then subsequently <code>man httpd_selinux</code> that the following will fix it:</p>
<pre>
chcon -R -h -t httpd_sys_script_exec_t /opt/ruby-enterprise-1.8.7-2009.10/lib/ruby/gems/1.8/gems/passenger-2.2.9/ext/apache2/mod_passenger.so
</pre>
<p>And so it does. &#8220;httpd_sys_script_exec_t&#8221; allows Apache to execute the SO, and we&#8217;re all good to restart.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2010/02/25/getting-phusion-passenger-to-run-under-selinux-on-centos-5-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>autocomplete sh: </title>
		<link>http://www.dansketcher.com/2010/01/15/autocomplete-sh-compgen-d-no-such-file-or-directory/</link>
		<comments>http://www.dansketcher.com/2010/01/15/autocomplete-sh-compgen-d-no-such-file-or-directory/#comments</comments>
		<pubDate>Fri, 15 Jan 2010 05:09:45 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/?p=117</guid>
		<description><![CDATA[Not much of a title, I know, but something for fellow stumped google-wanderers..
I&#8217;ve been playing around with deprec for automatically provisioning new servers for my Rails applications. However, I hit a strange problem where certain command-line auto-completions did not work; I&#8217;d get an error message like this:

$ ls
temp

$ cd [hit tab to get autocomplete]
-sh: ]]></description>
			<content:encoded><![CDATA[<p>Not much of a title, I know, but something for fellow stumped google-wanderers..</p>
<p>I&#8217;ve been playing around with deprec for automatically provisioning new servers for my Rails applications. However, I hit a strange problem where certain command-line auto-completions did not work; I&#8217;d get an error message like this:</p>
<pre>
$ ls
temp

$ cd [hit tab to get autocomplete]
-sh: <( compgen -d -- '' ): No such file or directory
-sh: <( compgen -f -X  -- '' ): No such file or directory
</pre>
<p>The confusing thing was that only the unpriviliged user exhibited this behaviour, not the root user.</p>
<p>I tried reinstalling the bash-completion package to no avail (even an aptitude purge then aptitude install did not work). Eventually I came across <a href="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=498474" onClick="javascript:urchinTracker ('/outbound/article/bugs.debian.org');">this rather long post on bugs.debian.org</a> where someone else has the same problem. Essentially it comes down to the unpriviliged user using /bin/sh as the default shell; under these circumstances autocompletion is supposed to not function (although presumably without error). This is despite /bin/sh being a symlink to /bin/bash</p>
<p>The fix, then, is actually quite simple. Change the default shell for all your unprivileged users to bash:</p>
<pre>
# usermod -s /bin/bash dansketcher
</pre>
<p>and then when you make new users, make sure you select bash explicitly:</p>
<pre>
# useradd newuser -m -s /bin/bash
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2010/01/15/autocomplete-sh-compgen-d-no-such-file-or-directory/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rails.cache with Memcache and a File cache for fragments</title>
		<link>http://www.dansketcher.com/2009/12/07/rails-cache-with-memcache-and-a-file-cache-for-fragments/</link>
		<comments>http://www.dansketcher.com/2009/12/07/rails-cache-with-memcache-and-a-file-cache-for-fragments/#comments</comments>
		<pubDate>Mon, 07 Dec 2009 05:54:22 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/?p=107</guid>
		<description><![CDATA[One of the things that we do at TouchLocal is provide Search Engine Optimised pages that drive traffic to the site through the UK National Directory pages. As you can imagine, there&#8217;s quite a lot of data intensive processing that is required to generate these pages, and there are an awful lot of them. As [...]]]></description>
			<content:encoded><![CDATA[<p>One of the things that we do at TouchLocal is provide Search Engine Optimised pages that drive traffic to the site through the <a href="http://www.touchlocal.com/nat/" onClick="javascript:urchinTracker ('/outbound/article/www.touchlocal.com');">UK National Directory</a> pages. As you can imagine, there&#8217;s quite a lot of data intensive processing that is required to generate these pages, and there are an awful lot of them. As it changes relatively rarely, for a long time we had been caching these pages in a Rails Action cache so that the headers and footers are not cached but the content is.</p>
<p>As we enhanced the caching strategy through the site, and the Rails support for Memcache improved, we changed the Rails cache to use Memcache. This by implication meant that the default cache we had been using for actions (the filestore cache) was no longer used and the infrastructure team could not flush the cache in the same way it previously had.</p>
<p>So that we could use the Rails.cache helper and still have the benefit of the Action cache, we changed our production environment configuration to look like this:</p>
<pre>
# Namespace for memcache to automatically flush on app version updates.
# APP_REVISION is updated automatically via asking the Version Control System
ns = "dir_#{RAILS_ENV}_#{$persona}_#{APP_REVISION}"

# MC_ADDRESSES constant is put into the root namespace - we use it for other things
::MC_ADDRESSES = ['10.0.0.1:11211', '10.0.0.2:11211', '10.0.0.3:11211']

# Rails.cache is memcache
config.cache_store = :mem_cache_store, MC_ADDRESSES,{:namespace => ns}

# Force the ActionController cache store to be the file one as per the previous default.
config.action_controller.cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache"
</pre>
<p>Happy days.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2009/12/07/rails-cache-with-memcache-and-a-file-cache-for-fragments/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Integrating Zeus ZXTM Load Balancer and Capistrano for Ruby on Rails websites</title>
		<link>http://www.dansketcher.com/2009/11/30/integrating-zeus-zxtm-load-balancer-and-capistrano-for-ruby-on-rails-websites/</link>
		<comments>http://www.dansketcher.com/2009/11/30/integrating-zeus-zxtm-load-balancer-and-capistrano-for-ruby-on-rails-websites/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 05:36:40 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/?p=105</guid>
		<description><![CDATA[At TouchLocal, one of the difficulties in automating deployment was the tight coupling between the app servers that were currently running and what the Load Balancer said was running. The implication of this is that at deployment time a human had to go into the ZXTM interface and update the list of running nodes before [...]]]></description>
			<content:encoded><![CDATA[<p>At TouchLocal, one of the difficulties in automating deployment was the tight coupling between the app servers that were currently running and what the Load Balancer said was running. The implication of this is that at deployment time a human had to go into the ZXTM interface and update the list of running nodes before and afterwards. To ease this, and allow fully automatic deployment of Ruby on Rails apps running under Phusion Passenger, I built the <a href="http://github.com/touchlocal/ruby_zxtm_manager" onClick="javascript:urchinTracker ('/outbound/article/github.com');">ZXTMManager</a> class.</p>
<p>This class implements an interface to the Zeus ZXTM API, allowing (in its current state) the ability to programatically change active nodes and manage other details of the commonly changed data in the Load Balancer. On top of that, there is an included deploy.rb file that shows the deployment recipes that can be simply and easily added to your own Capistrano configuration to use this class quickly and easily.</p>
<p>Enjoy.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2009/11/30/integrating-zeus-zxtm-load-balancer-and-capistrano-for-ruby-on-rails-websites/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using DataFabric to replace Masochism</title>
		<link>http://www.dansketcher.com/2009/11/25/using-datafabric-to-replace-masochism/</link>
		<comments>http://www.dansketcher.com/2009/11/25/using-datafabric-to-replace-masochism/#comments</comments>
		<pubDate>Wed, 25 Nov 2009 05:20:36 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/?p=103</guid>
		<description><![CDATA[When we upgraded from Rails 1.x to 2.x, we also had to migrate from Masochism to DataFabric. Unfortunately, the DataFabric way of doing things requires adding a declaration to each of the model classes. For a site like TouchLocal, where there are literally hundreds of model classes, this was a daunting task.
I&#8217;ve just gone through [...]]]></description>
			<content:encoded><![CDATA[<p>When we upgraded from Rails 1.x to 2.x, we also had to migrate from <a href="http://github.com/technoweenie/masochism" onClick="javascript:urchinTracker ('/outbound/article/github.com');">Masochism</a> to <a href="http://github.com/mperham/data_fabric" onClick="javascript:urchinTracker ('/outbound/article/github.com');">DataFabric</a>. Unfortunately, the DataFabric way of doing things requires adding a declaration to each of the model classes. For a site like TouchLocal, where there are literally hundreds of model classes, this was a daunting task.</p>
<p>I&#8217;ve just gone through extracting the magic that makes it possible to do this programatically, released as <a href="http://github.com/touchlocal/data_fabric_initializer" onClick="javascript:urchinTracker ('/outbound/article/github.com');">DataFabric::Initializer</a>. I hope it helps someone other than me!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2009/11/25/using-datafabric-to-replace-masochism/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Howto recover lost commits from a git rebase</title>
		<link>http://www.dansketcher.com/2009/11/23/howto-recover-lost-commits-from-a-git-rebase/</link>
		<comments>http://www.dansketcher.com/2009/11/23/howto-recover-lost-commits-from-a-git-rebase/#comments</comments>
		<pubDate>Mon, 23 Nov 2009 23:57:34 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/?p=94</guid>
		<description><![CDATA[Yesterday, I was working on a branch of a branch in a git repository, and I wanted to merge the last branch back to master. Following chapter 3.6 of ProGit, I ran

git rebase --onto master branch1 branch2

Unfortunately for me, I seem to have either done it from the wrong place or done the wrong thing [...]]]></description>
			<content:encoded><![CDATA[<p>Yesterday, I was working on a branch of a branch in a git repository, and I wanted to merge the last branch back to master. Following chapter <a href="http://progit.org/book/ch3-6.html" onClick="javascript:urchinTracker ('/outbound/article/progit.org');">3.6 of ProGit</a>, I ran</p>
<pre>
git rebase --onto master branch1 branch2
</pre>
<p>Unfortunately for me, I seem to have either done it from the wrong place or done the wrong thing for my situation, because git rewound branch2 to the revision that it was branched from master and all the later commits were inaccessible!</p>
<p>However, I was not going to give up quite so easily, and I figured that once a revision was committed, it had to be <em>somewhere</em>.. Fortunately for me, I manged to find this post on <a href="http://alexscordellis.blogspot.com/2009/07/how-to-recover-lost-commits-in-git.html" onClick="javascript:urchinTracker ('/outbound/article/alexscordellis.blogspot.com');">how to recover lost commits in a git repository</a> &#8211; I subsequently ran:</p>
<pre>
git reflog show
# find the commit with the last set of changes in your branch -
# good commit comments are so useful!
git co b6654bd
git co -b branch2_2
</pre>
<p>With my branch recovered, I did a simple rebase of the new (old) branch onto master and merged it, and all was well.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2009/11/23/howto-recover-lost-commits-from-a-git-rebase/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>can&#8217;t dup NilClass</title>
		<link>http://www.dansketcher.com/2009/05/11/cant-dup-nilclass/</link>
		<comments>http://www.dansketcher.com/2009/05/11/cant-dup-nilclass/#comments</comments>
		<pubDate>Mon, 11 May 2009 08:34:19 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/?p=68</guid>
		<description><![CDATA[I&#8217;m in the middle of upgrading an old Rails 1.2.6 app to Rails 2.3, and all of a sudden when logging in as a user, I get this:

TypeError in BusinessController#view

can't dup NilClass

C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2189:in `dup'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2189:in `scoped_methods'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2193:in `current_scoped_methods'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2183:in `scope'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1548:in `find_every'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1588:in `find_one'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1574:in `find_from_ids'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:616:in `find'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/belongs_to_association.rb:44:in `find_target'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_proxy.rb:240:in `load_target'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_proxy.rb:112:in `reload'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations.rb:1231:in `user'
C:/development/community_s0024_upgrade_upgrade/webapps/touchlocal/app/controllers/application_controller.rb:178:in `check_ticket_and_session'

Of course, that error means nothing as it is. What the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m in the middle of upgrading an old Rails 1.2.6 app to Rails 2.3, and all of a sudden when logging in as a user, I get this:</p>
<pre>
TypeError in BusinessController#view

can't dup NilClass

C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2189:in `dup'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2189:in `scoped_methods'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2193:in `current_scoped_methods'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2183:in `scope'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1548:in `find_every'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1588:in `find_one'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:1574:in `find_from_ids'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:616:in `find'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/belongs_to_association.rb:44:in `find_target'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_proxy.rb:240:in `load_target'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations/association_proxy.rb:112:in `reload'
C:/development/InstantRails-2.0-win/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations.rb:1231:in `user'
C:/development/community_s0024_upgrade_upgrade/webapps/touchlocal/app/controllers/application_controller.rb:178:in `check_ticket_and_session'
</pre>
<p>Of course, that error means nothing as it is. What the underlying cause is though, is that the class reloader is trying to unload a class but it can&#8217;t because of included modules that can&#8217;t be unloaded. The fix is to use the keyword <strong>unloadable</strong> in the model to tell the reloader to force a reload on each request:</p>
<pre>
class User < ActiveRecord::Base
  unloadable

  ...
end
</pre>
<p>All fixed!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2009/05/11/cant-dup-nilclass/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ruby Daemons and Vendoring</title>
		<link>http://www.dansketcher.com/2009/04/08/ruby-daemons-and-vendoring/</link>
		<comments>http://www.dansketcher.com/2009/04/08/ruby-daemons-and-vendoring/#comments</comments>
		<pubDate>Wed, 08 Apr 2009 12:08:31 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/?p=55</guid>
		<description><![CDATA[In the past in order to gather server side usage stats, we have had a procession of different methods to track and record this data in a way that was outside the main app. The reason for this separation is to ensure that search results on TouchLocal were as quick as possible while still recording [...]]]></description>
			<content:encoded><![CDATA[<p>In the past in order to gather server side usage stats, we have had a procession of different methods to track and record this data in a way that was outside the main app. The reason for this separation is to ensure that search results on TouchLocal were as quick as possible while still recording the details. The first attempt was to use <a href="http://stomp.codehaus.org/Home" onClick="javascript:urchinTracker ('/outbound/article/stomp.codehaus.org');">Stomp</a>, which was interesting but we had problems with stability. After that, and for a long time, we had a backend Merb application that was sent the tracking information from the webserver and returned immediately. While this was quick and pretty fast, it had the downside of the HTTP request from the main site which would time out and make the site crawl to a halt if the backend processes were not responding for whetever reason.</p>
<p>As a result, we went on a third rewrite effort. This time we went back to basics, and decided to use log4r to output a special log format that would be parsed and inserted into the tracking tables aynchronously. This would imply a delay, but only in the vicinity of a few minutes. The main design problems were ensuring stability of the processing platform and also the ability to retry files that might have been missed or errored. The repeatability was achieved by using a combination of a hash of the tracking data + the timestamp + some random information, and a (large) table that tracks this timestamps and hash combination (given that hashes can sometimes collide, it makes sense to add the timestamp as a factor).</p>
<p>The backend process to parse and record the information from this log file format was written in Ruby of course. In order to achieve stability I decided to use the Ruby <a href="http://daemons.rubyforge.org/" onClick="javascript:urchinTracker ('/outbound/article/daemons.rubyforge.org');">Daemons</a> gem. This handles PID file management and lots of other neat things for writing a daemon so the basics of long-running processes were not my &#8216;problem&#8217; as such. So that these Ruby processes could scale up, I ensured as I was writing it that it would be aware of the potential error conditions of multiple processes, such as one process moving a file when another was looking for it (race conditions, etc). While the Daemons gem uses the fork() implementation on *nix for the standard headless run mode, it also support a non-background run mode which works on Windows. I also chose to use ActiveRecord, reusing the AR models from the Merb application.</p>
<p>One of my personal goals was to ensure the daemon was as self-contained as possible. For me, this meant that I wanted the Sysadmin to be able to check it out and run the start command and have it work on a standard base Ruby installation. </p>
<p>Here&#8217;s the initialisation code for the Daemon:</p>
<pre>
################
### REQUIRES ###
################

# This loads gems in vendor/gems (abstracted out so it can be used in rake tasks that haven't loaded ENV yet)
require "lib/local_gem_loader"

require 'rubygems'
require 'daemons'
require 'activerecord'
require 'json/pure'

require 'erb'
require 'cgi'

# +require+ all the models
model_path = File.expand_path(File.join(File.dirname(__FILE__), 'models'))
$LOAD_PATH.unshift model_path
Dir.glob(File.join(model_path, '*.rb')) do |file|
  require file
end

# Log4r does not work because the Daemons gem closes all open file descriptors.
#require 'log4r'
#require 'config/logger'
#logger = ::DEFAULT_LOGGER

#################
### CONSTANTS ###
#################

SLEEP_TIME_SECONDS = 5
FILE_NAME = 'tracking_daemon.rb' # name to report as the process

#####################
### CONFIGURATION ###
#####################

database_configuration_file = File.expand_path(File.join(File.dirname(__FILE__), 'config', 'database.yml'))
database_configuration = YAML::load(ERB.new(IO.read(database_configuration_file)).result)

######################
### INITIALISATION ###
######################

# Parse out the RAILS_ENV=production setting
ARGV.each do |arg|
  if arg.include?('=')
    key, val = arg.split('=', 2)
    ENV[key] ||= val
  elsif database_configuration.keys.include?(arg)
    ENV['RAILS_ENV'] ||= arg
  end
end
RAILS_ENV = (ENV['RAILS_ENV'] || "development").dup
puts "Starting #{FILE_NAME} daemon in #{RAILS_ENV} mode"

ActiveRecord::Base.configurations = database_configuration
ActiveRecord::Base.establish_connection RAILS_ENV

root_files_path       = File.expand_path(File.join(File.dirname(__FILE__), 'files'))
incoming_files_path   = File.expand_path(File.join(root_files_path, 'incoming_files'))

options = {
             :multiple   => true,
             :ontop      => false,
             :backtrace  => true,
             :log_output => true,
             :monitor    => true
           }

##############
### DAEMON ###
##############

Daemons.run_proc(FILE_NAME, options) do
  loop do
    # 1. Get the next file to process
    Dir.glob(File.join(incoming_files_path, '*')) do |incoming_file|
    end

    #...

    puts "Sleep #{SLEEP_TIME_SECONDS} sec" if RAILS_ENV == "development"
    sleep(SLEEP_TIME_SECONDS)
  end
end
</pre>
<p>The local_gem_loader is something of my own invention, based on the vendor/gems loader that was introduced in Rails 2. I wrote it initially to enable our (then) Rails 1.2.6 app to have vendored gems. It was very useful here to allow me to meet my desire to have this thing be checked out from SVN and started. Here it is &#8211; it&#8217;s pretty simple really:</p>
<pre>
# Load the gems in /vendor/gems
standard_dirs = ['rails', 'plugins']
gems          = Dir[File.join(__FILE__, "vendor/*/**") ]
if gems.any?
  gems.each do |dir|
    next if standard_dirs.include?(File.basename(dir))
    lib = File.join(dir, 'lib')
    $LOAD_PATH.unshift(lib) if File.directory?(lib)
    src = File.join(dir, 'src')
    $LOAD_PATH.unshift(src) if File.directory?(src)
  end
end
</pre>
<p>After including that line, I was able to vendor all the gems I needed (activerecord, json-pure, and even daemons) in the vendor/gems directory I created. After that, the ./models directory is loaded with the lines</p>
<pre>
# +require+ all the models
model_path = File.expand_path(File.join(File.dirname(__FILE__), 'models'))
$LOAD_PATH.unshift model_path
Dir.glob(File.join(model_path, '*.rb')) do |file|
  require file
end
</pre>
<p>Also note that as per the behaviour of how Daemons is designed, as it starts it closes all open file descriptors. While I read this in the documentation, I still tried to integrate Log4r, and spent a very confused hour wondering why all my log files were erroring on write&#8230; anyhoo&#8230;</p>
<p>After ensuring the models are in the load path and are ready to go, ActiveRecord needs to be initialised. I added the ability to at runtime choose the environment to write to database-wise, just like Rails does. This is achieved here:</p>
<pre>
# First load the Rails config/database.yml
database_configuration_file = File.expand_path(File.join(File.dirname(__FILE__), 'config', 'database.yml'))
database_configuration = YAML::load(ERB.new(IO.read(database_configuration_file)).result)

# Parse out the RAILS_ENV=production setting, which can be either in the form
# ruby tracking_daemon.rb start RAILS_ENV=production or
# ruby tracking_daemon.rb start production
# Note that the environments allowed are vaildated against the ones available in the database.yml
ARGV.each do |arg|
  if arg.include?('=')
    key, val = arg.split('=', 2)
    ENV[key] ||= val
  elsif database_configuration.keys.include?(arg)
    ENV['RAILS_ENV'] ||= arg
  end
end

RAILS_ENV = (ENV['RAILS_ENV'] || "development").dup
puts "Starting #{FILE_NAME} daemon in #{RAILS_ENV} mode"

ActiveRecord::Base.configurations = database_configuration
ActiveRecord::Base.establish_connection RAILS_ENV
</pre>
<p>At the end of this we have loaded the database config and connected to the database. Happy days. The only thing left is to start the daemon, which I chose to do in an inline fashion. Note that Daemons allows you to have the process report whatever name you like in the process lists, but I went with the name of the file itself for clarity:</p>
<pre>
root_files_path       = File.expand_path(File.join(File.dirname(__FILE__), 'files'))
incoming_files_path   = File.expand_path(File.join(root_files_path, 'incoming_files'))

options = {
             :multiple   => true, # allow multiple concurrent of the same
             :ontop      => false, # daemonise
             :backtrace  => true, # show full failure info
             :log_output => true,
             :monitor    => true # instantiate a monitor to restart as required
           }

Daemons.run_proc(FILE_NAME, options) do
  loop do
    # 1. Get the next file to process
    Dir.glob(File.join(incoming_files_path, '*')) do |incoming_file|
    end

    #...

    puts "Sleep #{SLEEP_TIME_SECONDS} sec" if RAILS_ENV == "development"
    sleep(SLEEP_TIME_SECONDS)
  end
end
</pre>
<p>All in all it&#8217;s been a great success &#8211; what was previously using a few backend servers running full whack to process all the incoming information, was now using 2 daemons on a single server in each datacentre. They hardly even show up on the top list. Excellent stuff.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2009/04/08/ruby-daemons-and-vendoring/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Guide to Electronic Music</title>
		<link>http://www.dansketcher.com/2007/08/11/guide-to-electronic-music/</link>
		<comments>http://www.dansketcher.com/2007/08/11/guide-to-electronic-music/#comments</comments>
		<pubDate>Fri, 10 Aug 2007 14:23:37 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/2007/08/11/guide-to-electronic-music/</guid>
		<description><![CDATA[I found this Guide to Electronic Music &#8211; it&#8217;s so comprehensive it makes my head spin&#8230; but now when someone ask me what I think of weird music variants I&#8217;ll be able to give them an answer ;)
]]></description>
			<content:encoded><![CDATA[<p>I found this <a href="http://www.di.fm/edmguide/edmguide.html" onClick="javascript:urchinTracker ('/outbound/article/www.di.fm');">Guide to Electronic Music</a> &#8211; it&#8217;s so comprehensive it makes my head spin&#8230; but now when someone ask me what I think of weird music variants I&#8217;ll be able to give them an answer ;)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2007/08/11/guide-to-electronic-music/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wordpress &lt;code&gt; and &lt;pre&gt; formatting</title>
		<link>http://www.dansketcher.com/2007/06/07/wordpress-code-and-pre-formatting/</link>
		<comments>http://www.dansketcher.com/2007/06/07/wordpress-code-and-pre-formatting/#comments</comments>
		<pubDate>Thu, 07 Jun 2007 08:34:21 +0000</pubDate>
		<dc:creator>dansketcher</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.dansketcher.com/2007/06/07/wordpress-code-and-pre-formatting/</guid>
		<description><![CDATA[After the last paste-heavy post, I finally got around to doing something about wordpress&#8217;s formatting of code blocks. This site was very helpful:
http://tjulo.blogspot.com/2007/03/wordpress-and-source-code-posts.html
So, edit the file wp-includes/js/tinymce/plugins/wordpress/editor_plugin.js.
Comment out the section that replaces pre content:

/* var startPos = -1;
while ((startPos = content.indexOf('
]]></description>
			<content:encoded><![CDATA[<p>After the last paste-heavy post, I finally got around to doing something about wordpress&#8217;s formatting of code blocks. This site was very helpful:</p>
<p><a href="http://tjulo.blogspot.com/2007/03/wordpress-and-source-code-posts.html" onClick="javascript:urchinTracker ('/outbound/article/tjulo.blogspot.com');">http://tjulo.blogspot.com/2007/03/wordpress-and-source-code-posts.html</a></p>
<p>So, edit the file wp-includes/js/tinymce/plugins/wordpress/editor_plugin.js.</p>
<p>Comment out the section that replaces <em>pre</em> content:</p>
<pre>
/* var startPos = -1;
while ((startPos = content.indexOf('
<pre', var="" endpos="content.indexOf('<pr>">', startPos+1);
var innerPos = content.indexOf('>', startPos+1);
var chunkBefore = content.substring(0, innerPos);
var chunkAfter = content.substring(endPos);

var innards = content.substring(innerPos, endPos);
innards = innards.replace(/\n/g, '
');
content = chunkBefore + innards + chunkAfter;
}*/
</pre>
<p>Then I had to update my CSS so that <em>pre</em> used the same class as <em>code</em> and then change my posts over to use <em>pre</em> instead of <em>code</em>.</p>
<p>Next step is to fix <em>code</em> too so that I don't have to think about it.</p>
<p>It does seem to have the side-effect of not automatically handling the escaping of &lt; and &gt; symbols outside those tags though, which for this post where I started with &lt;code&gt; and &lt;pre&gt; instead of <em>code</em> and <em>pre</em>, made things a bit of a mess.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.dansketcher.com/2007/06/07/wordpress-code-and-pre-formatting/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.294 seconds -->
