<?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>Pathfinder Software &#187; Sharad Jain</title>
	<atom:link href="http://pathfindersoftware.com/author/sharad-jain/feed/" rel="self" type="application/rss+xml" />
	<link>http://pathfindersoftware.com</link>
	<description>The Fastest Way to Launch Successful Software</description>
	<lastBuildDate>Thu, 19 Jan 2012 16:31:04 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Installing mysql gem with bundler on Snow Leopard</title>
		<link>http://pathfindersoftware.com/2010/10/installing-mysql-gem-with-bundler-on-snow-leopard/</link>
		<comments>http://pathfindersoftware.com/2010/10/installing-mysql-gem-with-bundler-on-snow-leopard/#comments</comments>
		<pubDate>Thu, 07 Oct 2010 19:31:09 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[bundler]]></category>
		<category><![CDATA[gem]]></category>
		<category><![CDATA[MySql]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[snow leopard]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=5900</guid>
		<description><![CDATA[Between 0.9.26, RC and final 1.0 release, bundler went thru some heavy changes with respect to command line options it supports. Luckily, twitter, forum and blogs kept everybody in the loop. The blogs, however, became obsolete quickly and even blog posts few months old don&#8217;t work with latest version of bundler. We came across this ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><img src="http://pathfindersoftware.com/wp-content/uploads/3593686294_600ee1b7fb_z.jpg" style="float: right; margin: 10px; height: 250px;"/></p>
<p>Between 0.9.26, RC and final 1.0 release, <a href="http://gembundler.com/">bundler</a> went thru some heavy changes with respect to command line options it supports. Luckily, <a href="http://twitter.com/indirect">twitter</a>, <a href="http://groups.google.com/group/ruby-bundler">forum</a> and <a href="http://yehudakatz.com/tags/rails-3/">blogs</a> kept everybody in the loop.</p>
<p>The blogs, however, became obsolete quickly and even blog posts few months old don&#8217;t work with latest version of bundler. We came across this issue that kept is in a loop for a while.</p>
<p>Snow Leopard changed a few things with ruby and mysql gem when it came out. Most mysql installation issues are hammered out by now and are <a href="http://weblog.rubyonrails.org/2009/8/30/upgrading-to-snow-leopard">well documented</a>. In summary, installing mysql gem requires 2 things:</p>
<ul>
<li>Specifying architecture flags: ARCHFLAGS=&#8221;-arch x86_64&#8243;</li>
<li>Specify location of mysql_config to compile agains: &#8211;with-mysql-config=/usr/local/mysql/bin/mysql_config</li>
</ul>
<p><span id="more-5900"></span><br />
Prior to bundler, this is what you would do on snow-leopard to install mysql:</p>
<pre lang="bash">
$ sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
</pre>
<p>With bundler, there is no <a href="http://gembundler.com/man/bundle.1.html">command line option</a> that I could find that I can pass to &#8220;bundle install&#8221; command. Having been in the loop around bundler, I knew that such OS specific options don&#8217;t go in Gemfile and have to be specified in user specific files such as ~/.bundle/config file. The official website for <a href="http://gembundler.com/">gem-bundler</a> doesn&#8217;t have any mention of this (is it because we need to keep things simple for people just getting upto speed with bundler?).</p>
<p>A search for &#8220;build-options&#8221; (recalling from vague memory) <a href="http://www.samsworldofno.com/2010/01/04/installing-bundler-rails-and-mysql-on-os-x-snow-leopard/">turns up</a> <a href="http://github.com/schacon/bundler">these results</a>, which are all obsolete.</p>
<pre lang="bash">
# WARN: this doesn't work with latest bundler gem. Read on for proper instructions
$ gem bundle --build-options build_options.yml
</pre>
<p>After looping for a bit, I finally ended up at the <a href="http://gembundler.com/man/bundle-config.1.html">right place</a> (Google found this on gembundler.com but there is no way to get to this from the website itself). So, I did this:</p>
<pre lang="bash">
$ bundle config build.mysql --with-mysql-config=/usr/local/mysql/bin/mysql_config
</pre>
<p>This updated my ~/.bundle/config with proper information that it will need for &#8220;bundle install&#8221; command. It make sense to keep this in ~/ folder and not my project folder since it applies only to my environment and it applies to all projects on this machine.</p>
<p>With this, doing &#8220;bundle install&#8221; still doesn&#8217;t install mysql properly. Comparing this to the pre-bundler command, it solves one issue. The ARCHFLAGS still need to be available for &#8220;bundle install&#8221;. With some leap of faith, I set an environment variable to this effect:</p>
<pre lang="bash">
$ export ARCHFLAGS="-arch x86_64"
# should this go in my .bash_profile (so that mysql installs fine always, for all projects?
# And I don't have to remember to set this each time I do a fresh bundle install.
</pre>
<p>And voila, &#8220;bundle install&#8221; installs mysql correctly.</p>
<p>Bundler is stable now and is making everybody&#8217;s life much much easier. It isn&#8217;t changing as fast and so this blog post won&#8217;t be obsolete too soon <img src='http://pathfindersoftware.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /><br />
s
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;linkname=Installing%20mysql%20gem%20with%20bundler%20on%20Snow%20Leopard" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;linkname=Installing%20mysql%20gem%20with%20bundler%20on%20Snow%20Leopard" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;linkname=Installing%20mysql%20gem%20with%20bundler%20on%20Snow%20Leopard" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;linkname=Installing%20mysql%20gem%20with%20bundler%20on%20Snow%20Leopard" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;linkname=Installing%20mysql%20gem%20with%20bundler%20on%20Snow%20Leopard" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;linkname=Installing%20mysql%20gem%20with%20bundler%20on%20Snow%20Leopard" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;linkname=Installing%20mysql%20gem%20with%20bundler%20on%20Snow%20Leopard" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F10%2Finstalling-mysql-gem-with-bundler-on-snow-leopard%2F&amp;title=Installing%20mysql%20gem%20with%20bundler%20on%20Snow%20Leopard" id="wpa2a_2">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/10/installing-mysql-gem-with-bundler-on-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Monitoring rails processes (apache, passenger, delayed_job) using god and capistrano</title>
		<link>http://pathfindersoftware.com/2010/09/monitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano/</link>
		<comments>http://pathfindersoftware.com/2010/09/monitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano/#comments</comments>
		<pubDate>Wed, 01 Sep 2010 21:03:28 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[ruby rails god monitoring capistrano passenger delayed_job apache]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=5716</guid>
		<description><![CDATA[When it comes to monitoring servers and processes in production, there is quite a few open source tools: nagios and hyperic are proven solutions for enterprise apps. Monit and god are the favorites of ruby/rails community. God has been around for a few years now and most critical issues with god seem resolved. So, I ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<div style="float: right"><img style="border: 0px initial initial" src="http://pathfindersoftware.com/wp-content/uploads/2573042453_ff7a8c35d9.jpg" border="0" alt="fishing" width="250" /></a></div>
<p>When it comes to monitoring servers and processes in production, there is quite a few open source tools: <a href="http://www.nagios.org/">nagios</a> and <a href="http://www.hyperic.com/">hyperic</a> are proven solutions for enterprise apps. Monit and <a href="http://god.rubyforge.org/">god</a> are the favorites of ruby/rails community. God has been around for a few years now and most <a href="http://blog.bradgessler.com/use-monit-with-rails-not-god">critical</a> <a href="http://stackoverflow.com/questions/768184/god-vs-monit">issues</a> with god seem resolved. So, I decided to use that on my project.</p>
<p>If you&#8217;ve worked with any deployment related stuff before, you know that it hardly ever goes as planned. With few machines involved, few processes, file/folder permissions and process permissions, there is bound to be things that will take longer. God is no different. And no wonder you will find wiki/forum posts from folks who&#8217;ve been thru it. I had my share of those and in the end I am happy with the config I found.</p>
<p>Here is a journey as it went:<br />
<span id="more-5716"></span><br />
<strong>Goal-1: Monitor delayed_job using god:</strong></p>
<pre lang="ruby">
# run with: god -c /path/to/file.god -D
RAILS_ROOT = "/home/sjain/projects/pfa/omt"

# Watch for delayed_job
God.watch do |w|

  w.name = "delayed_job"
  w.interval = 30.seconds # default
  w.dir = RAILS_ROOT
  w.start = File.join(RAILS_ROOT, "script/delayed_job start")
  w.stop = File.join(RAILS_ROOT, "script/delayed_job stop")
  w.restart = File.join(RAILS_ROOT, "script/delayed_job restart")
  w.start_grace = 10.seconds
  w.restart_grace = 10.seconds
  w.pid_file = File.join(RAILS_ROOT, "tmp/pids/delayed_job.pid")
  w.log = File.join(RAILS_ROOT, 'log/god.log')

  w.behavior(:clean_pid_file)

  w.start_if do |on|
    on.condition(:process_running) do |c|
      c.interval = 5.seconds
      c.running = false
    end
  end

  # lifecycle stuff goes here ...
end
</pre>
<p>This is fairly standard from God&#8217;s website. The only interesting thing here is I kept RAILS_ROOT as a string constant at the top since I know I will have to make it configurable as part of my capistrano deploy script. With this, I could run following command and have god monitor my delayed job:</p>
<pre lang="bash">
$ god -c /var/www/html/application/current/config/app.conf
# Verify with 'god status'
</pre>
<p><strong>Goal-2: Monitor apache using god:</strong></p>
<pre lang="ruby">
# Watch for apache
God.watch do |w|

  watch_name = "app_server"
  w.name = watch_name
  w.interval = 30.seconds # default
  w.dir = RAILS_ROOT
  w.start = File.join(RAILS_ROOT, "/etc/init.d/httpd start")
  w.stop = File.join(RAILS_ROOT, "/etc/init.d/httpd stop")
  w.restart = File.join(RAILS_ROOT, "/etc/init.d/httpd restart")
  w.start_grace = 10.seconds
  w.restart_grace = 10.seconds

  w.start_if do |on|
    on.condition(:http_response_code) do |c|
      c.host = 'localhost'
      c.port = 3000
      c.path = '/'
      c.code_is_not = 200
      c.timeout = 10.seconds
      c.times = [2, 5]
    end
  end
end
</pre>
<p>This was a straight modification from the delayed_job config, which didn&#8217;t work for a few reasons:</p>
<p>1) Since I was starting god under my account, it couldn&#8217;t execute the start/stop commands specified in the watch file. For those to work, I had to start god script under sudo permissions.</p>
<pre lang="ruby">
$ sudo god -c /var/www/html/application/current/config/app.conf
# Once you start god as sudo, all other commands for god need sudo as well ... 'sudo god status'
</pre>
<p>2) The other issue with the above apache monitoring config was that once I start god as sudo, I still need my apache process to run under restricted user on the destination machine. Since we use CentOS for our production environment, this use is &#8216;www-data&#8217;. So, updated my god config with following:</p>
<pre lang="ruby">
# Watch for apache
God.watch do |w|
  ...
  w.uid = 'www-data'
  w.gid = 'www-data'
  ...
end
</pre>
<p>Also, now that I am starting god as sudo, all watches now need to specify uid/gid. So, I had to go back and update my delayed_job watch with uid/gid to be that of my &#8216;railsdeploy&#8217; user. My rails app gets deployed under &#8216;railsdeploy&#8217; user account and delayed_job process runs under this user as well.</p>
<pre lang="ruby">
# Watch for delayed_job
USER = "railsdeploy"
GROUP = "railsdeploy"

God.watch do |w|
  ...
  w.uid = USER
  w.gid = GROUP
  ...
end
</pre>
<p>Notice, how we are not hard-coding user/group directly. We know that eventually our capistrano script will need to replace those variables with configurable values. I kept all such things at the top of my .god file. There is a few more to come.</p>
<p>3) No, that was not all. The next problem I ran into was related to user environment. Since I am starting my god under sudo now, it was not guaranteed that my environment variables are still the same under that user. I found this the hard way (another 1/2 hr.) and in my case the default PATH under sudo was setup to give access to a different ruby version that the one we use for our production environment. So, I had to update both of my watches to have few necessary environment variables.</p>
<pre lang="ruby">
RAILS_ROOT = "/var/www/html/application/current"
RAILS_ENV = "sandbox"
RUBY_PATH = "/opt/ruby-enterprise/bin"

God.watch do |w|
  ...
  w.dir = RAILS_ROOT
  w.env = {
    'RAILS_ROOT' => RAILS_ROOT,
    'RAILS_ENV' => RAILS_ENV,
    'PATH' => "#{RUBY_PATH}:/usr/bin:/bin"
  }
  ...
end
</pre>
<p>A few tricks to remember when working with god is where to look for clues in case something doesn&#8217;t work. I found following things useful.</p>
<p>Adding log-level of debug when firing up god:</p>
<pre lang="ruby">
$ sudo god --log-level debug -c /var/www/html/application/current/config/app.conf
# with this god prints information each time it does a check on your monitored processes. These are big help to validate your frequency of poll and tweak your retry limits etc.
</pre>
<p>If you didn&#8217;t specify the log file location for god in command line, they go to system messages. This can be /var/log/messages on CentOS or /var/log/Syslog on Ubuntu (I guess). You can also specify log file in command line:</p>
<pre lang="ruby">
$ sudo god --log-level debug -c /var/www/html/application/current/config/app.conf -l /var/log/god.log
</pre>
<p>Or, during your test setup, you can run god in non-daemon mode to have it print all messages in console.</p>
<pre lang="ruby">
# -D option for non-daemon mode
$ sudo god --log-level debug -c /var/www/html/application/current/config/app.conf -D
</pre>
<p>So, with this, I have 2 processes being monitored and each with different user/group setting. So far, so good.</p>
<p><strong>Goal-3: Monitor passenger using god (failed):</strong></p>
<p>Since we run apache+passenger combo in our environments, I wanted to see if I can restart passenger also if http-response didn&#8217;t work as part of apache watch. So I attempted this:</p>
<pre lang="ruby">
# adding passenger to apache monitoring
God.watch do |w|
  ...
  apache_init = "/etc/init.d/httpd"
  passenger_restart = "touch #{RAILS_ENV}/tmp/restart.txt"
  w.start = "#{apache_init} start; #{passenger_restart}"
  w.stop = "#{apache_init} stop; #{passenger_restart}"
  w.restart = "#{apache_init} restart; #{passenger_restart}"
  ...
end
</pre>
<p>So, what I trying to do here is run 2 commands as part of one watch. Apparently, this doesn&#8217;t work. I believe, this has more to do with how god hands over those commands to under lying system that anything else. I couldn&#8217;t figure this one and I am not aware of the best way to monitor passenger outside of apache to I left it at that. My guess if the http-response is not working and I restart apache, it should technically restart passenger as well and fix the issue.</p>
<p><strong>Goal-4: Leverage capistrano to help with god configuration:</strong></p>
<p>Since I don&#8217;t even run apache locally for development (I run mongrel), I had to test all these things on a sandbox machine. I leveraged capistrano to help me out with deploying these god config files after I modified them each time locally. Here is what capistrano tasks looked like:</p>
<pre lang="ruby">
 namespace :god do
   task :start, :roles => :app do
     god_config_file = "#{latest_release}/config/omt.god"
     sudo "god --log-level debug -c #{god_config_file}"
   end
   task :stop, :roles => :app do
    sudo "god terminate" rescue nil
  end
  task :restart, :roles => :app do
    god.stop
    god.start
   end
   task :status, :roles => :app do
    sudo "god status"
   end
   task :log, :roles => :app do
    sudo "tail -f /var/log/messages"
   end
  task :deploy_config, :roles => :app do
     god_config_file = "#{latest_release}/config/omt.god"
     god_script_template = File.dirname(__FILE__) + "/deploy/templates/omt.god.erb.rb"
     data = ERB.new(IO.read(god_script_template)).result(binding)
     sudo "god load #{god_config_file}"
   end
   task :redeploy, :roles => :app do
     god.deploy_config
     god.load_config
   end
 end
</pre>
<p>Most tasks are straight forward with managing god itself (start/stop/restart/log). The deploy_config tasks worked great with the following config file in order to generate proper .god config file on the destination machine:</p>
<pre lang="ruby">
# run with: god -c /path/to/file.god -D
RAILS_ROOT = "<%= latest_release %>"
RAILS_ENV = "<%= rails_env %>"
USER = "<%= user %>"
GROUP = "<%= group %>"
RUBY_PATH = "<%= ruby_home %>/bin"
GOD_ENVIRONMENT = {
  'RAILS_ROOT' => RAILS_ROOT,
  'RAILS_ENV' => RAILS_ENV,
  'PATH' => "#{RUBY_PATH}:/usr/bin:/bin"
}

God.watch do |w|
  ...
  w.dir = RAILS_ROOT
  w.env = GOD_ENVIRONMENT
  w.uid = USER
  w.gid = GROUP
  ...
end
</pre>
<p>As you can see above, the constants defined at the top of .god.erb.rb template file picked values for variables from the capistrano script and replace them in template to generate the actual .god config file. Now, I could use this file to generate script for each environment and have it generate this with each god deploy. The god script once configured doesn&#8217;t change often but I still like to regenerate it with each capistrano deploy just as I would generate apache vhost file using capistrano with each deploy. This is debatable but I just like it that way.</p>
<p><strong>Goal-5: Configure god as init script</strong></p>
<p>Any monitoring solution isn&#8217;t complete until it takes care of starting/stopping itself automatically. God has to be configured to start automatically on server reboot. Now, it was time to create init script for god itself. After a few trial errors I ended up with following init script template:</p>
<pre lang="bash">
#!/bin/bash
#
# God
#
# chkconfig: - 99 1
# description: start, stop, restart God (bet you feel powerful)
#

# source function library
. /etc/rc.d/init.d/functions

RUBY_PATH="<%= ruby_home %>/bin"
PATH=$RUBY_PATH:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=$RUBY_PATH/god
PIDFILE=/var/run/god.pid
LOGFILE=/var/log/god.log
SCRIPTNAME=/etc/init.d/god
CONFIGFILEDIR=/etc/god

#DEBUG_OPTIONS="--log-level debug"
DEBUG_OPTIONS=""

# Gracefully exit if 'god' gem is not available.
test -x $DAEMON || exit 0

RETVAL=0

god_start() {
  start_cmd="$DAEMON -l $LOGFILE -P $PIDFILE $DEBUG_OPTIONS"
  #stop_cmd="kill -QUIT `cat $PIDFILE`"
  echo $start_cmd
  $start_cmd || echo -en "god already running"
  RETVAL=$?
  if [ "$RETVAL" == '0' ]; then
    sleep 2 # wait for server to load before loading config files
    if [ -d $CONFIGFILEDIR ]; then
      for file in `ls -1 $CONFIGFILEDIR/*.god`; do
        echo "god: loading $file ..."
        $DAEMON load $file
      done
    fi
  fi
  return $RETVAL
}

god_stop() {
  stop_cmd="god terminate"
  echo $stop_cmd
  $stop_cmd || echo -en "god not running"
}

case "$1" in
  start)
    god_start
    RETVAL=$?
    ;;
  stop)
    god_stop
    RETVAL=$?
    ;;
  restart)
    god_stop
    god_start
    RETVAL=$?
    ;;
  status)
    $DAEMON status
    RETVAL=$?
    ;;
  *)
    echo "Usage: god {start|stop|restart|status}"
    exit 1
    ;;
esac

exit $RETVAL
</pre>
<p>A few things to note here. If you look carefully at the god_start() function, it starts the god process as daemon and then loads any *.god files in /etc/god/ folder. This has several benefits. When the server reboots, god doesn&#8217;t need to hunt around for config files in various places. And whatever application needs to be monitored can have one of their config files installed here for god to pickup. The capistrano deploy can now be updated to deploy a .god file in this folder and then call &#8220;god load app.god&#8221; to have it loaded during each deploy. The next time machine/god restarts, it will pick up monitoring of configured apps automatically.</p>
<p>The capistrano task could look like:</p>
<pre lang="ruby">
namespace :god do
  task :deploy_config, :roles => :app do
    # deploy omt.god file
    god_config_file = "#{latest_release}/config/omt.god"
    god_script_template = File.dirname(__FILE__) + "/deploy/templates/omt.god.erb.rb"
    data = ERB.new(IO.read(god_script_template)).result(binding)
    put data, god_config_file
    sudo "mkdir -p /etc/god", :pty => true
    sudo "ln -sf #{god_config_file} /etc/god/omt.god", :pty => true
    # load file into god service (assumes god is already running)
    god_config_file = "#{latest_release}/config/omt.god"
    sudo "god load #{god_config_file}"
  end
end
</pre>
<p>Since each deployed environment (sandbox, staging, production) can have ruby-home in different folder location. I couldn&#8217;t use static init script file. I leveraged capistrano to use a template file (with ERB engine) to replace variables on the fly. The capistrano task for this looks as follows:</p>
<p>Even deploying god init script file could be done using capistrano:</p>
<pre lang="ruby">
namespace :god do
  task :deploy_init_script, :roles => :app do
    # Note: god gem (version >= 0.11.0) must be installed as system gem for this to work
    god_init_template = File.dirname(__FILE__) + "/deploy/templates/god.init.template.sh"
    data = ERB.new(IO.read(god_init_template)).result(binding)
    put(data, "/tmp/god_init_script", :via => :scp, :mode => "755") # put command doesn't support sudo, so we can't directly copy to /etc/init.d folder
    sudo "mv /tmp/god_init_script /etc/init.d/god"
    sudo "/sbin/chkconfig --level 35 god on" # enable run levels 3 and 5
  end
end
</pre>
<p>As you can see above, we copy the init script in /etc/init.d/god folder and then run chkconfig command to have it restart automatically in unix run-levels 3 and 5. Now, assuming god is already setup on your server to run as init script. We can modify our capistrano task for god start/stop/restart/status commands to leverage unix&#8217;s standard &#8220;service&#8221; command.</p>
<pre lang="ruby">
# capistrano tasks: god:start, god:stop, god:restart, god:status
namespace :god do
  ["start", "stop", "restart", "status"].each do |cmd|
    task cmd.to_sym, :roles => :app do
      sudo "service god #{cmd}"
    end
  end
end
</pre>
<p>There is still a few more things I have to left to do. For example, monitoring  mysql database server. Since my database in production resides on a different server than app server, this will require installing a different script on mysql server machine or somehow monitor remotely from one of the app servers&#8230;</p>
<p>As I mentioned previously, deployment tasks never go as planned. There are always hiccups. But it is fun to see it all working at the end of the day!
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;linkname=Monitoring%20rails%20processes%20%28apache%2C%20passenger%2C%20delayed_job%29%20using%20god%20and%20capistrano" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;linkname=Monitoring%20rails%20processes%20%28apache%2C%20passenger%2C%20delayed_job%29%20using%20god%20and%20capistrano" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;linkname=Monitoring%20rails%20processes%20%28apache%2C%20passenger%2C%20delayed_job%29%20using%20god%20and%20capistrano" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;linkname=Monitoring%20rails%20processes%20%28apache%2C%20passenger%2C%20delayed_job%29%20using%20god%20and%20capistrano" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;linkname=Monitoring%20rails%20processes%20%28apache%2C%20passenger%2C%20delayed_job%29%20using%20god%20and%20capistrano" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;linkname=Monitoring%20rails%20processes%20%28apache%2C%20passenger%2C%20delayed_job%29%20using%20god%20and%20capistrano" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;linkname=Monitoring%20rails%20processes%20%28apache%2C%20passenger%2C%20delayed_job%29%20using%20god%20and%20capistrano" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F09%2Fmonitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano%2F&amp;title=Monitoring%20rails%20processes%20%28apache%2C%20passenger%2C%20delayed_job%29%20using%20god%20and%20capistrano" id="wpa2a_4">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/09/monitoring-rails-processes-apache-passenger-delayed_job-using-god-and-capistrano/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Using Bundler with rails 2.3.X</title>
		<link>http://pathfindersoftware.com/2010/06/using-bundler-with-rails-2-3-x/</link>
		<comments>http://pathfindersoftware.com/2010/06/using-bundler-with-rails-2-3-x/#comments</comments>
		<pubDate>Sat, 12 Jun 2010 01:14:37 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=5223</guid>
		<description><![CDATA[Bundler is optional for rails 2.3.X. However, it is not a bad idea to upgrade to bundler for several reasons: It is stable now and will solve your gem dependency conflicts better than rubygems require. You will be better prepared to migrate to rails3 when you are ready. Instructions for using bundler with rails 2.3 ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><img src="http://pathfindersoftware.com/wp-content/uploads/2386181060_06d0fbbaa6.jpg" style="float: right; height; 200px; width: 100px;"/><br />
<a href="http://gembundler.com/">Bundler</a> is optional for rails 2.3.X. However, it is not a bad idea to upgrade to bundler for several reasons:</p>
<ul>
<li>It is stable now and will solve your gem dependency conflicts better than rubygems require.</li>
<li>You will be better prepared to migrate to rails3 when you are ready.</li>
</ul>
<p>Instructions for using <a href="http://gembundler.com/rails23.html">bundler with rails 2.3</a> will get you off to a good start. We use <a href="http://github.com/thoughtbot/shoulda">shoulda</a> and <a href="http://github.com/floehopper/mocha">mocha</a> for our test suite. If you do too, you may run into an issue that Yehuda rightly blogged about <a href="http://yehudakatz.com/2010/04/17/ruby-require-order-problems/">here</a>.<br />
<span id="more-5223"></span></p>
<p>To fix this, here is what you need to do:</p>
<p>In config/boot.rb, replace:</p>
<pre lang="ruby">
    def load_gems
        @bundler_loaded ||= Bundler.require :default, Rails.env
    end
</pre>
<p>with:</p>
<pre lang="ruby">
      def load_gems
        @bundler_loaded ||= begin
          result = Bundler.require(:default)
          Bundler.require(Rails.env) unless Rails.env.test?
          result
        end
      end
</pre>
<p>And in your config/environments/test.rb, add following at the end:</p>
<pre lang="ruby">
require "shoulda"
Bundle.require(:test)
</pre>
<p>As Yehuda <a href="http://yehudakatz.com/2010/04/17/ruby-require-order-problems/">explains</a>, Mocha adds a few features depending on weather shoulda is loaded or not. The trick is to ensure that shoulda is loaded and is available before mocha is loaded.</p>
<p>Besides, if you are using mocha version 0.9.8, and have a &#8216;require &#8220;mocha&#8221;&#8216; line at the bottom of your test/test_helper.rb (as required by <a href="http://github.com/floehopper/mocha">mocha/README</a>), you can probably remove that here. Bundler takes care of it properly.</p>
<p>Again, don&#8217;t forget that non-rails commands must be executed with bundle exec:</p>
<pre lang="ruby">
$ bundle exec cucumber
</pre>
<p>Enjoy!
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;linkname=Using%20Bundler%20with%20rails%202.3.X" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;linkname=Using%20Bundler%20with%20rails%202.3.X" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;linkname=Using%20Bundler%20with%20rails%202.3.X" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;linkname=Using%20Bundler%20with%20rails%202.3.X" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;linkname=Using%20Bundler%20with%20rails%202.3.X" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;linkname=Using%20Bundler%20with%20rails%202.3.X" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;linkname=Using%20Bundler%20with%20rails%202.3.X" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F06%2Fusing-bundler-with-rails-2-3-x%2F&amp;title=Using%20Bundler%20with%20rails%202.3.X" id="wpa2a_6">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/06/using-bundler-with-rails-2-3-x/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Login as multiple users simultaneously for testing</title>
		<link>http://pathfindersoftware.com/2010/05/login-as-multiple-users-simultaneously-for-testing/</link>
		<comments>http://pathfindersoftware.com/2010/05/login-as-multiple-users-simultaneously-for-testing/#comments</comments>
		<pubDate>Thu, 27 May 2010 01:44:42 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[testing rails browser]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=5197</guid>
		<description><![CDATA[Ever wondered how to login as two different users at the same time? The situation manifests in any project where you want to be able to test a workflow involving several actors with different roles. The problem is that browsers allow only one login for a given domain at any one time. So, you are ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><img src="http://pathfindersoftware.com/wp-content/uploads/4028160820_0292417df2.jpg"></p>
<p>Ever wondered how to login as two different users at the same time? The situation manifests in any project where you want to be able to test a workflow involving several actors with different roles. The problem is that browsers allow only one login for a given domain at any one time. So, you are required to logout as current_user before logging in as another. One of the solution people use is install multiple browsers.</p>
<p><span id="more-5197"></span></p>
<p><strong>Solution: Multiple Browsers</strong></p>
<p>So, lets say your favorite browser is firefox. And you also have IE7 installed by default on your windows machine. And you have a google chrome installed just so you can try out the new and blazing contribution by google. Most developers, I assume, more than a single browser installed on their system. Given this, you can login as different user in each browser.</p>
<p>This works but here is another solution that, as you will quickly realize, is better.</p>
<p><strong>Solution: Multiple names for your &#8220;localhost&#8221;</strong></p>
<p>Just update your /etc/hosts file to add more names for your local machine. Lets take a concrete example:</p>
<p>So, on a project, realted to mortgage industry, I have several actors like borrower, realtor, loan officer, mortgage-broker. I updated my /etc/hosts file (on linux) from this line:</p>
<pre>
127.0.0.1 localhost
</pre>
<p>to this:</p>
<pre>
127.0.0.1 localhost r lo mb b
</pre>
<p>With this above line, I can access my local application using following URLs:</p>
<p>http://r:3000 (for realtor)<br />
http://lo:3000 (for loan officer)<br />
http://b:3000 (for borrower)<br />
http://mb:3000 (for mortage broker)</p>
<p>Now, I can open 4 tabs in a browser each with different URL and login as different users. Coupled this with password saving feature of browser, I don&#8217;t even have to type username/password for each user as long as I am disciplined about not cross-logging in as realtor inside borrower url (b:3000). Go ahead, leave yourself logged in and never logout!</p>
<p>This simple trick is better than multiple browser solution since it doesn&#8217;t require as many browser and you can have as many unique URLs as you want. And you are not limited to using this trick for local testing. If you have a staging server at http://staging.project.com that resolves to an IP, say, 192.1.2.3, you can repeat the same process:</p>
<pre>
# my /etc/hosts file
127.0.0.1 localhost r lo mb b
192.1.2.3 staging.project.com sr slo smb sb
# Now I can
# http://sr (staging realtor)
# http://slo (staging loan officer)
# you get the drill ...
</pre>
<p>There you go. Happy testing!
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;linkname=Login%20as%20multiple%20users%20simultaneously%20for%20testing" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;linkname=Login%20as%20multiple%20users%20simultaneously%20for%20testing" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;linkname=Login%20as%20multiple%20users%20simultaneously%20for%20testing" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;linkname=Login%20as%20multiple%20users%20simultaneously%20for%20testing" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;linkname=Login%20as%20multiple%20users%20simultaneously%20for%20testing" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;linkname=Login%20as%20multiple%20users%20simultaneously%20for%20testing" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;linkname=Login%20as%20multiple%20users%20simultaneously%20for%20testing" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Flogin-as-multiple-users-simultaneously-for-testing%2F&amp;title=Login%20as%20multiple%20users%20simultaneously%20for%20testing" id="wpa2a_8">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/05/login-as-multiple-users-simultaneously-for-testing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Disabling form post in google chrome and safari</title>
		<link>http://pathfindersoftware.com/2010/05/disabling-form-post-in-google-chrome-and-safari/</link>
		<comments>http://pathfindersoftware.com/2010/05/disabling-form-post-in-google-chrome-and-safari/#comments</comments>
		<pubDate>Mon, 17 May 2010 22:38:32 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=5179</guid>
		<description><![CDATA[So, I found this issue with google chrome and safari web browser and I believe this applies to any webkit based browser. When a form submit is disabled by setting the submit button to disabled=true attribute, the browser is usually expected to prevent submission when the user hits &#8220;ENTER&#8221; on any of its text fields. ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><img class="alignleft" src="http://pathfindersoftware.com/wp-content/uploads/Google-Chrome-Logo.jpg" alt="" width="236" height="224" /></p>
<p>So, I found this issue with google chrome and safari web browser and I believe this applies to any webkit based browser. When a form submit is disabled by setting the submit button to disabled=true attribute, the browser is usually expected to prevent submission when the user hits &#8220;ENTER&#8221; on any of its text fields. This works fine in Firefor, my default browser.</p>
<p>In my rails app, I had to do this to work around this.</p>
<p>We use <a href="http://haml.hamptoncatlin.com/">haml</a> rendering engine for our project and my form looks like this:</p>
<pre>
- remote_form_for @product_search, :url =&gt; product_searches_path, |
  :html =&gt; {:method =&gt; :post, :id =&gt; 'product_search_form'} do |form|
</pre>
<p><span id="more-5179"></span><br />
This generates a form like this:</p>
<pre>
  &lt;form onsubmit="jQuery.ajax({data:jQuery.param(jQuery(this).serializeArray())
  + '&amp;authenticity_token='
  + encodeURIComponent('MlHm/z3dsdYtETKwtNnwQlojx1NIduLlWFMECbjntsM='),
     dataType:'script', type:'post', url:'/product_searches'});
     return false;"
  method="post" id="product_search_form"
  class="product_search"
  action="/product_searches"&gt;
</pre>
<p>I was working with an ajax form but I believe the bug happens even with traditional form POST as reported by another user <a href="http://code.google.com/p/chromium/issues/detail?id=37402">here</a>.</p>
<p>So, I had to create an onSubmit javascript myself to fix the situation:</p>
<pre>
- semantic_form_for @product_search, :url =&gt; product_searches_path, |
  :html =&gt; {:method =&gt; :post, :id =&gt; 'product_search_form', |
  ::onsubmit =&gt; "return jQuery.productSearchOnSubmit();"} do |form| |
</pre>
<pre>
(function($){
  $.productSearchOnSubmit = function() {
    if ($('#product_search_submit').attr('disabled')) {
    // do nothing
    } else {
      jQuery.ajax(
      {
        data: jQuery.param(jQuery('#product_search_form').serializeArray()) + '&amp;authenticity_token=' +
        encodeURIComponent(form_authenticity_token),
        dataType:'script',
        type:'post',
        url:'/product_searches'
      });
    }
    return false;
  }
})(jQuery);
</pre>
<p>I also had to add global javascript variable for form_authenticity_token to make it available for javascript when performing submit.</p>
<pre>
%html
  %head
    :javascript
      var form_authenticity_token = '#{form_authenticity_token}';
</pre>
<p>If you think, this is a <a href="http://code.google.com/p/chromium/issues/detail?id=37402">bug</a> too, please vote for a fix.
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;linkname=Disabling%20form%20post%20in%20google%20chrome%20and%20safari" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;linkname=Disabling%20form%20post%20in%20google%20chrome%20and%20safari" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;linkname=Disabling%20form%20post%20in%20google%20chrome%20and%20safari" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;linkname=Disabling%20form%20post%20in%20google%20chrome%20and%20safari" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;linkname=Disabling%20form%20post%20in%20google%20chrome%20and%20safari" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;linkname=Disabling%20form%20post%20in%20google%20chrome%20and%20safari" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;linkname=Disabling%20form%20post%20in%20google%20chrome%20and%20safari" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F05%2Fdisabling-form-post-in-google-chrome-and-safari%2F&amp;title=Disabling%20form%20post%20in%20google%20chrome%20and%20safari" id="wpa2a_10">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/05/disabling-form-post-in-google-chrome-and-safari/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rails Email Unit Testing</title>
		<link>http://pathfindersoftware.com/2010/04/rails-email-unit-testing/</link>
		<comments>http://pathfindersoftware.com/2010/04/rails-email-unit-testing/#comments</comments>
		<pubDate>Fri, 23 Apr 2010 23:39:38 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=5104</guid>
		<description><![CDATA[ActionMailer classes, for sending out emails, typically reside with models, inside app/models folder. Yet, they have traits that are similar to controllers. For example, each actionmailer class has a view folder inside app/views where email templates are defined. And testing support for email is provided as functional tests with assert_select macros such as assert_select_email. In ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><img src="http://pathfindersoftware.com/wp-content/uploads/265899766_bbf8b7e5d9.jpg"></p>
<p>ActionMailer classes, for sending out emails, typically reside with models, inside app/models folder. Yet, they have traits that are similar to controllers. For example, each actionmailer class has a view folder inside app/views where email templates are defined. And testing support for email is provided as functional tests with assert_select macros such as <a href="http://guides.rubyonrails.org/testing.html#testing-views">assert_select_email</a>.</p>
<p>In my current project, most of our email hooks are defined on model classes as after_filter. This is in line with recommendation to keep controllers thin and models fat. However, testing email body&#8217;s html format is a pain since assert_select_email support is only available in ActionController::TestCase hierarchy, and not in ActionMailer::TestCase hierarchy.</p>
<p><span id="more-5104"></span><br />
Consider following ActionMailer test:</p>
<pre>
class MortgageBorrowerEmailerTest &lt; ActionMailer::TestCase
  should &quot;invite borrowers&quot; do
    realtor, borrower = User.make, User.make
    response = MortgageBorrowerEmailer.deliver_invite_borrowers(realtor, borrower)
    # test from/to/subject works now <img src='http://pathfindersoftware.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />
    assert_same_elements([borrower.email], response.to)
    assert_equal([realtor.email], response.from)
    assert_equal(&quot;#{realtor.full_name} has invited you to manage your mortgage application in Lendable&quot;, response.subject)
    # testing body is still primitive <img src='http://pathfindersoftware.com/wp-includes/images/smilies/icon_sad.gif' alt=':-(' class='wp-smiley' />
    assert response.body.include?(realtor.full_name)
    assert response.body.include?(&quot;/account_preferences/#{borrower.perishable_token}/edit&quot;)
  end
end
</pre>
<p>ActionMailer::TestCase has decent support for intercepting mail delivery and hold onto mails for introspection. This allows us to assert smaller pieces like from, to, subject etc. However, what it doesn&#8217;t support is more fine grained assertion on email&#8217;s html body. it would be nice to be able to apply assert_select assertion on email&#8217;s response body for critical pieces of information that needs to be present in an email.</p>
<p>So, I dug into ActionController::TestCase&#8217;s assert_select_email to see how I can duplicate it for my unit test. Here is what the original method looks like:</p>
<pre>
# [rails]/actionpack/lib/action_controller/assertions/selector_assertions.rb
module ActionController::Assertions::SelectorAssertions
  def assert_select_email(&amp;block)
    deliveries = ActionMailer::Base.deliveries
    assert !deliveries.empty?, "No e-mail in delivery list"
    for delivery in deliveries
      for part in delivery.parts
        if part["Content-Type"].to_s =~ /^text/htmlW/
          root = HTML::Document.new(part.body).root
          assert_select root, ":root", &amp;block
        end
      end
    end
  end
end
</pre>
<p>As this implementation depicts, it is interesting to see how easy it is to take any html code snipped and create an HTML::Document out of it so that assert_select can be applied to it. Anyway, the default implementation for assert_select_email assumed multipart email with one or more parts in it. Since my email are simplistic text/html rendered as email body, I hacked to get what I needed. As follows:</p>
<pre>
class MortgageBorrowerEmailerTest &lt; ActionMailer::TestCase
  include ActionController::Assertions::SelectorAssertions
  def assert_select_with_string(html_text, &amp;block)
    root = HTML::Document.new(html_text).root
    assert_select root, ":root", &amp;block
  end
  ...
end
</pre>
<p>With this in place, I could now take my email response&#8217;s body and assert-select on it:</p>
<pre>
should "share mortgage with existing client" do
  ...
  assert_select_with_string(response.body) do
    assert_select "h1", :text =&gt; "Greetings from Lendable"
    assert_select "tr#sender_identity td strong", :text =&gt; @realtor.full_name
    assert_select "tr#rate_search_criteria" do
      assert_select ".purchase_price"
      assert_select ".loan_amount"
    end
    assert_select "tr#rate_info td a[href=?]", "http://#{DEFAULT_HOST}/rate_selections/#{@rate_selection.id}",
      :text =&gt; "#{@rate.program.type_and_term} Mortgage at #{number_with_precision(@rate.rate, :precision =&gt; 3)}% from #{@rate.program.lender.name}"
  end
end
</pre>
<p>As we can imagine, this assert_select_with_string() can be useful in testing helper methods that return html snippets. I am not sure what the downside is for testing email body format as unit test vs. funcational test but I am happy that I can test it in isolation as unit test.</p>
<p>Do you know of any?
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;linkname=Rails%20Email%20Unit%20Testing" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;linkname=Rails%20Email%20Unit%20Testing" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;linkname=Rails%20Email%20Unit%20Testing" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;linkname=Rails%20Email%20Unit%20Testing" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;linkname=Rails%20Email%20Unit%20Testing" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;linkname=Rails%20Email%20Unit%20Testing" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;linkname=Rails%20Email%20Unit%20Testing" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Frails-email-unit-testing%2F&amp;title=Rails%20Email%20Unit%20Testing" id="wpa2a_12">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/04/rails-email-unit-testing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Internet, Email, Blogs, Forums &#8230; did we need twitter?</title>
		<link>http://pathfindersoftware.com/2010/04/internet-email-blogs-forums-did-we-need-twitter/</link>
		<comments>http://pathfindersoftware.com/2010/04/internet-email-blogs-forums-did-we-need-twitter/#comments</comments>
		<pubDate>Fri, 16 Apr 2010 00:35:46 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=5043</guid>
		<description><![CDATA[Yes, we did. And here&#8217;s why. I was at a recent social gathering where we started talking about how funny and bizzare it is that people use twitter to post messages like .. sh**, woke up late .. having lunch at Gino&#8217;s East &#8230; driving to work &#8230; my dog peed on my bed &#8230; ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><img src="http://pathfindersoftware.com/wp-content/uploads/3703187062_8caebcca2f.jpg" alt="" width="418" height="253" /><br />
Yes, we did. And here&#8217;s why.</p>
<p>I was at a recent social gathering where we started talking about how funny and bizzare it is that people use twitter to post messages like .. sh**, woke up late .. having lunch at Gino&#8217;s East &#8230; driving to work &#8230; my dog peed on my bed &#8230; . .. Oh ok, that last one is probably out of thin air but posting those kinds of messages probably don&#8217;t add much value for anybody, the author or the readers.</p>
<p>To test the waters, I created an account about 6 months ago. Very quickly I realized that twitter fills in a void that none of the other communication tools do.</p>
<p><span id="more-5043"></span></p>
<p><strong>Keep in touch, I mean real touch:</strong><br />
We don&#8217;t make it a point to write email to every single one on our list of 200 or 500 contacts every once in a while. Why? Because you will have to walk down the list, write to each person individually. We would have to come up with something to talk about that this person might be interested in. Too much work. But we still want to peek into their life and vice versa to find out if there is anything that interests both of us. Twitter fits the bill. You tweet (by definition, short) about anything that touches your life. If the other person is interested in you, he/she will follow you and vaguely peruse over your tweets. You do the same and follow him. Even if you no longer meet each other (because one of you has moved to different city or different job), you can talk to each other over this channel. If everybody in my group did this, I&#8217;d be fully aware of what happens with them on a day by day, hour by hour basis without actually calling them up. And vice versa.</p>
<p>Another great advantage that isn&#8217;t apparent at first is that twitter allows me to follow person who I am not associated with directly but what he does has a large impact on my life. Since I am programming in Ruby these days, I want to know what ruby gurus (<a href="http://twitter.com/dhh">@dhh</a>, <a href="http://twitter.com/wycats">@wycats</a>) are upto each day. If you are a java programmer, you may want to follow <a href="http://twitter.com/SpringRod">RodJohnson</a>. These guys (I thank them) tweet few times a day and knowing that DHH and his team is upto each day is huge&#8230; it is gold mine!</p>
<p><strong>Mass conversation:</strong><br />
Twittering is like sitting at a bar with folks that share your interests and talk about interesting things, just that it happens in a very loosely coupled way. All the same conversation rules apply. You want to be having light and funny yet intelligent conversation without having to invest the time to walk to a bar. And you can be in multiple bars at the same time. It doesn&#8217;t replace the bar meetups but it does make it easier to have more of those in our daily life, a good thing isn&#8217;t it.</p>
<p><strong>You are never alone, even when you are alone:</strong><br />
We always had internet to keep us busy surfing and killing time when alone. One problem with plain vanilla internet surfing is that you have manually visit several websites to dig relevant pieces out that are important to you. Twitter makes it easier to subscribe to channels (people or companies or search tags) that are relevant and in very short message bursts gives you lots of superficial information. This is great for quickly perusing on channels and then dig deeper into stuff that interests you. With twitter account and a smartphone, those 5 minute wait at bus station, 10 minute wait at restaurant could be good investment in keeping in touch.</p>
<p><strong>Books, Articles, Blogs and now Twitter:</strong><br />
We couldn&#8217;t write books every month, so we started writing articles. We couldn&#8217;t write articles each week, so we invented blogs. We can&#8217;t write blogs every day, so we have twitter. It is just a very low cost way to get the message across.</p>
<p>After using it for a while here are some things I learnt as far as twitter etiquettes are concerned:</p>
<p><strong>Keep audience engaged:</strong><br />
My leg is hurting &#8230; nice weather &#8230; are boring tweets. They don&#8217;t initiate interesting conversation. Instead what I&#8217;ve found useful is to post an opinion on some news you just heard. Or some article, blog that you came across or commenting on a live TV cast of Presidents&#8217; speech. Ask quick yes/no type questions although not always. Talk about pair programming .. how about twitting a quick question to you programmer colleague on how to solve a pesky problem? beat that!</p>
<p><strong>Post often but not too often:</strong><br />
Posting 15, 20, 30 tweets a day on an account is good. Anything more is annoyance and you run the risk of loosing your followers. Posting less isn&#8217;t good either. It isn&#8217;t blog or article after all. Its purpose is to keep in touch &#8230; how about a tweet an hour on average.</p>
<p><strong>Have separate accounts:</strong><br />
You need separate accounts to match each of your personalities. By personalities, I mean you are a professional at work, music rockstar in your school circle and lazy bum in your family circle. Sometimes the differences in these personalities are so stark that you don&#8217;t want to mix those. For example, my mom and sister will have no interest what so ever in my ruby programming tweets. However, my colleague at work aren&#8217;t really interested (much) in my hindi movie reviews and cricket fan club conversations. Having separate accounts keeps things simple.</p>
<p>All in all, the concept of twitting is here to stay!</p>
<p>Certainly, a great innovation. Does twitter.com hold a patent for this <img src='http://pathfindersoftware.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;linkname=Internet%2C%20Email%2C%20Blogs%2C%20Forums%20%26%238230%3B%20did%20we%20need%20twitter%3F" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;linkname=Internet%2C%20Email%2C%20Blogs%2C%20Forums%20%26%238230%3B%20did%20we%20need%20twitter%3F" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;linkname=Internet%2C%20Email%2C%20Blogs%2C%20Forums%20%26%238230%3B%20did%20we%20need%20twitter%3F" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;linkname=Internet%2C%20Email%2C%20Blogs%2C%20Forums%20%26%238230%3B%20did%20we%20need%20twitter%3F" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;linkname=Internet%2C%20Email%2C%20Blogs%2C%20Forums%20%26%238230%3B%20did%20we%20need%20twitter%3F" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;linkname=Internet%2C%20Email%2C%20Blogs%2C%20Forums%20%26%238230%3B%20did%20we%20need%20twitter%3F" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;linkname=Internet%2C%20Email%2C%20Blogs%2C%20Forums%20%26%238230%3B%20did%20we%20need%20twitter%3F" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F04%2Finternet-email-blogs-forums-did-we-need-twitter%2F&amp;title=Internet%2C%20Email%2C%20Blogs%2C%20Forums%20%26%238230%3B%20did%20we%20need%20twitter%3F" id="wpa2a_14">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/04/internet-email-blogs-forums-did-we-need-twitter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Optimizing has_role? in acl9</title>
		<link>http://pathfindersoftware.com/2010/03/optimizing-has_role-in-acl9-2/</link>
		<comments>http://pathfindersoftware.com/2010/03/optimizing-has_role-in-acl9-2/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 04:11:24 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=4921</guid>
		<description><![CDATA[acl9 is a an authorization library for rails applications. It is one of the widely used library if not the most widely used now. Our experience with acl9 shows that it might be heavy weight if your authorization needs are simpler (which most projects are) but could be useful for other projects. If you&#8217;ve used ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><img src="http://pathfindersoftware.com/wp-content/uploads/235453953_b565f23939_m_d.jpg" alt="" style="float: right" height="240" border="1"></p>
<p><a href="http://github.com/be9/acl9">acl9</a> is a an authorization library for rails applications. It is one of the widely used library if not the most widely used <a href="http://www.ruby-toolbox.com/categories/rails_authorization.html">now</a>. Our experience with acl9 shows that it might be heavy weight if your authorization needs are simpler (which most projects are) but could be useful for other projects.</p>
<p>If you&#8217;ve used <a href="http://www.acegisecurity.org/">acegi/spring-security</a> for authorization in your java apps, you know that acl9 is very similar in principle and hence very powerful. In addition to primary roles, it provides object level permissions which are stored in a generic way separately from the objects being controlled, all without the need for handcoding/distributing your authorization columns in each authorization-object tables.</p>
<p>One place where acl9 differs from acegi is how it doesn&#8217;t differentiate between a role and a permission. Acegi signifies roles as global permission level which allows you to do certain things (some action on any object of a given class). Where as, a &#8220;permission&#8221; controls whether your can take that action on a certain object of a class or not. Acl9 calls them all &#8220;roles&#8221; (primary-roles and object-roles). As you can imagine, a given user may have a few roles in system but end up with lot and lots of permissions in system depending on how many objects user owns  etc. This may seem like good idea at first but it presents a unique problem which is not apparent at first. Since roles and permissions are not conceptually separate in acl9 &#8211; and that a user can have lots of them (few roles and lots of permissions) &#8211; prevents us from loading and caching them in memory. Why do we need to keep them in memory? Because you are querying user&#8217;s primary roles most often in your rendering of pages.</p>
<p>For example, consider navigation-bar which is common in most applications. Different users are presented with different tabs in navigation-bar and this bar gets rendered on each request/response cycle. Whether to render a particular tab is conditional to whether a user has certain role (primary role in particular) or not. Since acl9 cannot keep all roles (and permissions) in memory, it has to perform database query every time it has to find whether a user has_role?(admin) or not. Given that there can be only a few primary-roles that the user will have in any system, it seems in-efficient to not cache them and go to database each time.</p>
<p>The solution would be to separate these primary-roles from permission-roles and cache them for each request. In acl9 this means overriding User.has_role? and user.has_role!.</p>
<pre>
class User
  def has_role?(role, object = nil)
    if object || !Role.primary?(role)
      super
    else
      primary_roles.collect(&amp;:name).include?(role.to_s)
    end
  end

  def has_role!(role, object = nil)
    super
    @primary_roles = extract_primary_roles if(Role.primary?(role))
  end

  def primary_roles
    @primary_roles ||= extract_primary_roles
  end

  def extract_primary_roles
     self.role_objects.select { |r| r.primary? }
  end
  private :extract_primary_roles
end
</pre>
<p>That does it. You cache the primary-roles and leverage those for has_role? queries.
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;linkname=Optimizing%20has_role%3F%20in%20acl9" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;linkname=Optimizing%20has_role%3F%20in%20acl9" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;linkname=Optimizing%20has_role%3F%20in%20acl9" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;linkname=Optimizing%20has_role%3F%20in%20acl9" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;linkname=Optimizing%20has_role%3F%20in%20acl9" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;linkname=Optimizing%20has_role%3F%20in%20acl9" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;linkname=Optimizing%20has_role%3F%20in%20acl9" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F03%2Foptimizing-has_role-in-acl9-2%2F&amp;title=Optimizing%20has_role%3F%20in%20acl9" id="wpa2a_16">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/03/optimizing-has_role-in-acl9-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Unit Testing Sphinx</title>
		<link>http://pathfindersoftware.com/2010/02/unit-testing-sphinx/</link>
		<comments>http://pathfindersoftware.com/2010/02/unit-testing-sphinx/#comments</comments>
		<pubDate>Sun, 07 Feb 2010 01:28:17 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=4776</guid>
		<description><![CDATA[Sphinx (and its rails plugin thinking-sphinx) is my choice of search engine on ruby/rails project. It is powerful yet super easy to setup. However, testing Sphinx code is not easy at first. Since Sphinx works by leverging database commit hooks, it cannot be tested within the bounds of unit testing framework that rails provides. This ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p>Sphinx (and its rails plugin thinking-sphinx) is my choice of search engine on ruby/rails project. It is powerful yet super easy to setup.</p>
<p>However, testing Sphinx code is not easy at first. Since Sphinx works by leverging database commit hooks, it cannot be tested within the bounds of unit testing framework that rails provides. This is understandable because, in rails testing, a transaction is started before each test that is bound to rollback after the test is finished. Since the test data is never committed, sphinx doesn&#8217;t get a chance to index anything and cannot be tested.</p>
<p>The documentation for <a href="http://freelancing-god.github.com/ts/en/testing.html">sphinx testing</a> suggests using cucumber for integration testing. To me, cucumber test are still miles away from the smallest piece of sphinx code (inside Model) to be tested. So, I turned to <a href="http://github.com/rails/rails/blob/master/activerecord/test/cases/transactions_test.rb">how transactional code is tested in rails</a> framework for some cue.</p>
<p>Here is what I ended up with:</p>
<pre lang="ruby">
class TransactionalUserTest < ActiveSupport::TestCase
  // any transactional test needs to have this
  self.use_transactional_fixtures = false

  context "with no users in database" do
    setup do
      // clear the existing data for our test - not sure if this affects other test but we use machinist instead of fixture files, so we should be good here.
      User.destroy_all
      UserProfile.destroy_all
    end

    context "with a few users created" do
      setup do
        @john = @david = nil
        // any data for sphinx test should be wrapped in transaction so sphinx can see these changes
        User.transaction do
          @john = User.make(:first_name => "John")
          @david = User.make(:first_name => "David")
        end
      end
      should "find user with first name john" do
        // start sphinx server
        ThinkingSphinx::Test.run do
          // give sphinx an opportunity to index newly added data (required before calling search)
          ThinkingSphinx::Test.index
          assert_equal([@john], User.search("john").collect)
          assert_equal([@david], User.search("david").collect)
          assert_equal([],User.search("cheese").collect)
        end
      end
    end
  end
end
</pre>
<p>Isn&#8217;t it nicer to be able to test sphinx code in isolation <img src='http://pathfindersoftware.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;linkname=Unit%20Testing%20Sphinx" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;linkname=Unit%20Testing%20Sphinx" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;linkname=Unit%20Testing%20Sphinx" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;linkname=Unit%20Testing%20Sphinx" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;linkname=Unit%20Testing%20Sphinx" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;linkname=Unit%20Testing%20Sphinx" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;linkname=Unit%20Testing%20Sphinx" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F02%2Funit-testing-sphinx%2F&amp;title=Unit%20Testing%20Sphinx" id="wpa2a_18">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/02/unit-testing-sphinx/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ruby/Rails: Loading Seed Data for Tests</title>
		<link>http://pathfindersoftware.com/2010/01/rubyrails-loading-seed-data-for-tests/</link>
		<comments>http://pathfindersoftware.com/2010/01/rubyrails-loading-seed-data-for-tests/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 18:16:40 +0000</pubDate>
		<dc:creator>Sharad Jain</dc:creator>
				<category><![CDATA[Software Development]]></category>

		<guid isPermaLink="false">http://www.pathf.com/blogs/?p=4548</guid>
		<description><![CDATA[Fixtures are notorious in rails. To get around issues like brittleness and multiple file flipping to understand single test, there have been better approaches using gems like fixture_replacement, machinist and factory_girl. No complains there. In my experience, fixture are still great for one thing: loading seed data. This is because seed data is often used ...]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;source=PathSoft&amp;style=normal&amp;service=bit.ly&amp;service_api=R_8a1154b608af9e55718b231fb0025d40&amp;b=2" height="61" width="50" /><br />
			</a>
		</div>
<p><img style="float: right; border-color: black; border: 2px; margin: 2px" src="http://pathfindersoftware.com/wp-content/uploads/Eo-scale_of_justice.gif" alt="" /><br />
<a href="http://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures">Fixtures</a> are notorious in rails. To get around issues like brittleness and multiple file flipping to understand single test, there have been better approaches using gems like <a href="http://replacefixtures.rubyforge.org/">fixture_replacement</a>, <a href="http://github.com/notahat/machinist">machinist</a> and <a href="http://github.com/thoughtbot/factory_girl">factory_girl</a>. No complains there. In my experience, fixture are still great for one thing: loading seed data. This is because seed data is often used by many many tests and they don&#8217;t change often and hence won&#8217;t cause tests to be brittle. Another great advantage of fixtures is that any fixture data is loaded once and only once for the entire test run.</p>
<p>Rails 2.3.4 includes a new rake task for loading seed data called db:seed. It suggests keeping all seed data as a ruby code inside of db/seeds.rb file. The issue with this is this is not DRY. You have duplicate set of representation for seed data: one side of seeds.rb and one in fixture files (.yml). Keeping them in sync is a pain and all the other disadvantages that come with not having single source of truth.</p>
<p><strong>Problem</strong></p>
<p>To have single source of seed data and be able to load that seed data once and only once for the entire test run.</p>
<p><span id="more-4548"></span></p>
<p><strong>Solution 1: Use Fixtures:</strong></p>
<p><em>Advantage:</em> Fixture are ideal since fixture files (.yml) get loaded once and only one before the tests start running.</p>
<p><em>Disadvantage:</em> requires duplicating seed data that is already coded in db/seeds.rb into .yml files. We might try to get around this by having another rake task that converts data inside seeds.rb into .yml files or vice versa. In any case, it feels like a hack.</p>
<p><strong>Solution 2: Load seeds.rb as part of test</strong></p>
<pre lang="ruby"># test/test_helper.rb
module TestHelper
  def load_seed_data
    load File.dirname(__FILE__) + '/../db/seeds.rb'
  end
end

class ActiveSupport::TestCase
  include TestHelper
end</pre>
<p><em>Advantage:</em> loads the seed data from same seeds.rb and eliminates duplicating them into fixture files (.yml).</p>
<p><em>Disadvantage:</em> load_seed_data has to be called once before each single test and data is loaded and discarded as part of each test. This impacts test runs greatly and is not scalable as codebase and hence tests grow.</p>
<p><strong>The Solution: call db:seed as part of db:test:prepare</strong></p>
<p>Rails executes db:test:prepare when tests are run. This solution entails adding a hook to run db:seed as part of this. This ensures that the seed data from seeds.rb is loaded before the tests start. One caveat here is disable fixture loaded by deleting all .yml files, otherwise it will delete the seed data loaded here.</p>
<pre lang="ruby">namespace :db do
  namespace :test do
    task :prepare =&gt; :environment do
      Rake::Task["db:seed"].invoke
    end
  end
end</pre>
<p><em>Advantage:</em> Re-uses seeds.rb, loads seed data into test database only once for entire test run</p>
<p><em>Disadvantage:</em> none so far.</p>
<p><strong>Caveats:</strong></p>
<ul>
<li>While running individual tests or test files (as part of invoking test from inside IDE), we need to ensure that test database is already loaded with seed data. This can be easily done using &#8220;db:reset&#8221; rake task on test environment.</li>
<li>The .yml files have to be deleted from test/fixtures folder. If any .yml file exists, rails will delete the data from the table corresponding to that file as part of test setup. This will happen even if the file is empty and does not contain any seed data.</li>
</ul>
<p>Clean and DRY. That feels good.
<p><a class="a2a_button_linkedin" href="http://www.addtoany.com/add_to/linkedin?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;linkname=Ruby%2FRails%3A%20Loading%20Seed%20Data%20for%20Tests" title="LinkedIn" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/linkedin.png" width="16" height="16" alt="LinkedIn"/></a><a class="a2a_button_stumbleupon" href="http://www.addtoany.com/add_to/stumbleupon?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;linkname=Ruby%2FRails%3A%20Loading%20Seed%20Data%20for%20Tests" title="StumbleUpon" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/stumbleupon.png" width="16" height="16" alt="StumbleUpon"/></a><a class="a2a_button_digg" href="http://www.addtoany.com/add_to/digg?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;linkname=Ruby%2FRails%3A%20Loading%20Seed%20Data%20for%20Tests" title="Digg" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/digg.png" width="16" height="16" alt="Digg"/></a><a class="a2a_button_dzone" href="http://www.addtoany.com/add_to/dzone?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;linkname=Ruby%2FRails%3A%20Loading%20Seed%20Data%20for%20Tests" title="DZone" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/dzone.png" width="16" height="16" alt="DZone"/></a><a class="a2a_button_reddit" href="http://www.addtoany.com/add_to/reddit?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;linkname=Ruby%2FRails%3A%20Loading%20Seed%20Data%20for%20Tests" title="Reddit" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/reddit.png" width="16" height="16" alt="Reddit"/></a><a class="a2a_button_delicious" href="http://www.addtoany.com/add_to/delicious?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;linkname=Ruby%2FRails%3A%20Loading%20Seed%20Data%20for%20Tests" title="Delicious" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/delicious.png" width="16" height="16" alt="Delicious"/></a><a class="a2a_button_evernote" href="http://www.addtoany.com/add_to/evernote?linkurl=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;linkname=Ruby%2FRails%3A%20Loading%20Seed%20Data%20for%20Tests" title="Evernote" rel="nofollow" target="_blank"><img src="http://pathfindersoftware.com/wp-content/plugins/add-to-any/icons/evernote.png" width="16" height="16" alt="Evernote"/></a><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fpathfindersoftware.com%2F2010%2F01%2Frubyrails-loading-seed-data-for-tests%2F&amp;title=Ruby%2FRails%3A%20Loading%20Seed%20Data%20for%20Tests" id="wpa2a_20">Share/Bookmark</a></p>
]]></content:encoded>
			<wfw:commentRss>http://pathfindersoftware.com/2010/01/rubyrails-loading-seed-data-for-tests/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic (User agent is rejected)
Page Caching using memcached (User agent is rejected)

Served from: pathfindersoftware.com @ 2012-02-09 13:56:47 -->
