<?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>overwatering &#187; ruby</title>
	<atom:link href="http://www.overwatering.org/blog/category/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.overwatering.org/blog</link>
	<description>Random musings on fish, books and occasionally programming.</description>
	<lastBuildDate>Mon, 07 Nov 2011 07:06:02 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>A modest proposal towards reusable step definitions for Cucumber</title>
		<link>http://www.overwatering.org/blog/2011/10/a-modest-proposal-towards-reusable-step-definitions-for-cucumber/</link>
		<comments>http://www.overwatering.org/blog/2011/10/a-modest-proposal-towards-reusable-step-definitions-for-cucumber/#comments</comments>
		<pubDate>Wed, 05 Oct 2011 04:35:25 +0000</pubDate>
		<dc:creator>giles</dc:creator>
				<category><![CDATA[computing]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[work]]></category>
		<category><![CDATA[cucumber]]></category>

		<guid isPermaLink="false">http://www.overwatering.org/blog/?p=382</guid>
		<description><![CDATA[



While the title is a nod to the original modest proposal this is completely serious. I just liked the title. Pez thought I should be more clear.




Typically, cucumber step definitions look like this:

When /^I select the recipient named "([^"]*)"$/ do &#124;recipientname&#124;
 within_jqm_page do
   page.find('a', :text =&#62; recipientname).click
 end
end


Step definitions implemented like this end [...]]]></description>
			<content:encoded><![CDATA[<div align="center">

<div style="text-align: left; width: 80%; background-color: rgb(255, 255, 153); font-size: 65%; padding-bottom: 10px; padding-left: 10px; padding-right: 10px;">

While the title is a nod to the original <a href="http://en.wikipedia.org/wiki/A_Modest_Proposal">modest proposal</a> this is completely serious. I just liked the title. <a href="http://twitter.com/perrynfowler">Pez</a> thought I should be more clear.

</div>
</div>

<p>Typically, <a href="http://cukes.info/">cucumber</a> step definitions look like this:</p>

<pre><code>When /^I select the recipient named "([^"]*)"$/ do |recipientname|
 within_jqm_page do
   page.find('a', :text =&gt; recipientname).click
 end
end
</code></pre>

<p>Step definitions implemented like this end up becoming a pain to re-use. Essentially you are calling a function, except to do so you have to construct a string, and then execute that. We&#8217;ve all seen how that ends.</p>

<pre><code>Given /^all the entered recipient details are valid$/ do
  When %{I fill in "name" with "Valid Name"}
  And %{I fill in "phone" with "4152234567"}
  And %{I fill in "email1" with "valid.email@example.com"}
end
</code></pre>

<p>The direct outcome is very hard to maintain step definitions &mdash; like the above &mdash; and thus very poor quality cucumber tests. Patterns like <a href="http://blog.josephwilk.net/cucumber/page-object-pattern.html">page objects</a> and <a href="http://pilchardfriendly.wordpress.com/2011/09/25/test-personas-a-pattern-for-acceptance-tests/">personas</a> take work to extract when following this approach. This approach that is analogous to writing VB6 applications in the early 2000s where all logic was just a double-click on a form button away. And as with VB6, that cucumber directly encourages this approach is part of the problem.</p>

<p>I hear a lot of complaints about cucumber these days. These complaints tend to target the English language test scripts: while business readable acceptance tests may be nice, these are useless if the business is never actually reading the tests.</p>

<p>The unspoken heart of this complaint seems to be the English language tests are responsible for the poor maintainability of the average cucumber test suite. I agree. But that shouldn&#8217;t be the case.</p>

<p>After trying to build well-factored cucumber suites on a few occasions, I pretty much hate the pattern of just attaching anonymous blocks to regular expressions. Instead, I want step definitions that directly invoke methods. Something like:</p>

<pre><code>Given /^I am on the (\w+) page$/ =&gt; :go_to_known_page,
  :on =&gt; { current_user }

class User
  def self.current_user
    …
  end

  def go_to_known_page(page_name)
    visit Page.page_named(page_name)
  end
end

World(User)
</code></pre>

<p>The significant part is in the first two lines. Instead of passing a block to the <code>Given</code>, a symbol for a method to invoke would be passed. This method would be invoked with the arguments matched by the regular expression, after any <code>Transform</code>s have been applied. The <code>:on</code> is optional. This is a block evaluated against the <code>World</code>. If <code>:on</code> is provided, the method would be sent to the result of this block instead.</p>

<p>Yes, this would force you to define classes and attach methods to those. Yes, this would require you to think about the interrelationships of your objects. Yes, this is quite a lot more limited than an arbitrary block of code. Yes, that is exactly the point.</p>

<p>After a quick glance through the cucumber source this does not look difficult to add. Should I go to the effort of adding it?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.overwatering.org/blog/2011/10/a-modest-proposal-towards-reusable-step-definitions-for-cucumber/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Patterns for Bundler and rvm</title>
		<link>http://www.overwatering.org/blog/2011/03/patterns-for-bundler-and-rvm/</link>
		<comments>http://www.overwatering.org/blog/2011/03/patterns-for-bundler-and-rvm/#comments</comments>
		<pubDate>Tue, 01 Mar 2011 11:49:25 +0000</pubDate>
		<dc:creator>giles</dc:creator>
				<category><![CDATA[computing]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[work]]></category>
		<category><![CDATA[ruby bundler rvm]]></category>

		<guid isPermaLink="false">http://www.overwatering.org/blog/?p=367</guid>
		<description><![CDATA[A couple of long standing problems in the world of Ruby have been dealing with all the gems that are so easy to toss into your project and the differences between Ruby implementations. They do differ: between 1.8.6 and 1.8.7 standard library APIs changed. Slow clap.

In the world of adults the solution to these problems [...]]]></description>
			<content:encoded><![CDATA[<p>A couple of long standing problems in the world of Ruby have been dealing with all the gems that are so easy to toss into your project and the differences between Ruby implementations. They do differ: between 1.8.6 and 1.8.7 standard library APIs changed. Slow clap.</p>

<p>In the world of adults the solution to these problems is known as dependency management. There are now two tools to help Ruby: <a href="http://rvm.beginrescueend.com">rvm</a> and <a href="http://gembundler.com">Bundler</a>. These are both opinionated pieces of software that try to do a lot to help. Often far too much. After digging quite deeply into Bundler with <a href="http://pilchardfriendly.wordpress.com/">Nick Drew</a> on a recent project, here are some things that we learnt that I&#8217;ve tried to distill into a set of guides on good ways to use Bundler and rvm. This is just what I&#8217;ve discovered, if there are any tips or better ways to use these tools, or other tools that help, please share.</p>

<p>This is going to assume that you already know something about both.</p>

<ul>
<li>Firstly, use rvm and Bundler. If you have used them in the past and were unhappy, try again. The early versions had some truly horrible bugs. Development has moved quickly and things are a lot better now. If you&#8217;re using Rails 3 then you have little choice about Bundler. But for any projects you should use these.</li>
<li>Use rvm to install your Ruby, don&#8217;t use the system Ruby. Create at least one gemset and use that.</li>
<li><p>Create a <code>.rvmrc</code> for your project and check it into source control. Naming the gemset after your project, it should probably look something like:</p>

<pre><code>rvm_install_on_use_flag=1
rvm --create use ruby-1.8.7-p330@project
</code></pre></li>
<li><p>Use rvm on your build box. But not in production.</p></li>
<li>Create a <code>personal</code> gem set and toss the gems you use on simple little scripts into that.</li>
<li>Install Bundler into the global gemset for the Ruby you are using.</li>
<li>Include <code>development</code> and <code>test</code> groups in your <code>Gemfile</code>. Be strict about the difference between a gem required to run the build system (development), used in tests (test — obviously) and a gem required to run the application.</li>
<li>Don&#8217;t check the <code>.bundle/config</code> file into source control. Bundler often chooses to remember settings in this file. This will cause much irritation. This is an example of being more helpful than is helpful.</li>
<li>Don&#8217;t use the <code>--deployment</code> switch to <code>bundle install</code> unless you really mean that. As well as producing a local bundle of gems, installing in deployment mode will freeze the Gemfile. This is remembered and will cause much frustration when you&#8217;re trying to add new gems. If this does happen you will get an opaque error complaining that you have not checked your <code>Gemfile.lock</code> into source control. To get around this, just delete your <code>.bundle/config</code>. You&#8217;re doing that to remove the remembered setting about freezing your gems.</li>
<li><p>Your build script (the one you run before committing) should call <code>bundle package</code>. In fact, it should look quite a lot like:</p>

<pre><code>bundle package &amp;&amp; bundle install &amp;&amp; rake spec features
</code></pre></li>
<li><p>Check the gem files in <code>vendor/cache</code> into source control.</p></li>
<li>Definitely use <code>bundle install --deployment --local --without="development test"</code> either as part of your Capistrano tasks or as part of packaging for a release. Every one of those switches except the first should not be required. But they are. So there you go.</li>
<li>Bundler attempts to take over your world. This is intensely irritating. Expect to be annoyed.</li>
<li><p>If you ever need to start a Ruby program from your Ruby program but in a different Bundler world, you will need to do some cleaning of your environment. In particular you will need to clean the environment variables <code>BUNDLE_GEMFILE</code>, <code>BUNDLE_PATH</code>, <code>BUNDLE_BIN_PATH</code> and most surprisingly <code>RUBYOPT</code>. You may find the following function helpful.</p>

<pre><code>def without_bundler_env
  original_env = ENV.to_hash
  ENV.delete("BUNDLE_GEMFILE")
  ENV.delete("BUNDLE_PATH")
  ENV.delete("BUNDLE_BIN_PATH")
  ENV.delete("RUBYOPT")
  yield
ensure
  ENV.replace(original_env.to_hash)
end
</code></pre></li>
<li><p>If you need to examine the <code>Gemfile</code> from another Ruby project in your Bundler controlled world, you will think that you can use <code>Bundler::Definition.build("&lt;i&gt;path to Gemfile&lt;/i&gt;", "&lt;i&gt;path to Gemfile.lock&lt;/i&gt;", nil)</code>. That would be a good start. You will also need to set <code>ENV["BUNDLE_GEMFILE"]</code> to point to that file and call <code>resolve_with_cache!</code>, however. Try the following:</p>

<pre><code>ENV['BUNDLE_GEMFILE'] = 'Gemfile'
bundle_def = Bundler::Definition.build('Gemfile',
                                       'Gemfile.lock',
                                       nil)
bundle_def.resolve_with_cache!
</code></pre></li>
</ul>

<p>Ultimately, the goal of Bundler and rvm are noble. Dependency management is a hard problem. And there are no alternatives if you are choosing to use Ruby. Are there things that could perhaps be done better? Absolutely. But there is probably nothing so bad that some patterns couldn&#8217;t fix. So please, let&#8217;s all work out how to get the most out of these tools.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.overwatering.org/blog/2011/03/patterns-for-bundler-and-rvm/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Find Mutual Follows, Redux</title>
		<link>http://www.overwatering.org/blog/2009/03/find-mutual-follows-redux/</link>
		<comments>http://www.overwatering.org/blog/2009/03/find-mutual-follows-redux/#comments</comments>
		<pubDate>Thu, 26 Mar 2009 13:49:31 +0000</pubDate>
		<dc:creator>giles</dc:creator>
				<category><![CDATA[application]]></category>
		<category><![CDATA[comp. sci.]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.overwatering.org/blog/?p=125</guid>
		<description><![CDATA[Previously, I&#8217;ve mentioned that I&#8217;ve written a script that will show a Twitter user who they and another Twitter user both follow.

Well, a script isn&#8217;t much use to me or anyone else: you can&#8217;t run a script on an iPhone. So like all software wants to be, this script is now a simple little web [...]]]></description>
			<content:encoded><![CDATA[<p>Previously, I&#8217;ve <a href="http://www.overwatering.org/blog/2009/01/finding-mutual-follows/">mentioned</a> that I&#8217;ve written a script that will show a <a href="http://twitter.com">Twitter</a> user who they and another Twitter user both follow.</p>

<p>Well, a script isn&#8217;t much use to me or anyone else: you can&#8217;t run a script on an iPhone. So like all software wants to be, this script is now a simple little web application: <a href="http://mutual.overwatering.org">mutual</a>. Please be gentle, and I know I&#8217;m not a designer.</p>

<p>This should be the start of more useful little applications like this appearing. I&#8217;ll blog about that. Also, for the programmers out there, this is written using the <a href="http://www.sinatrarb.com">Sinatra</a> Ruby web application framework. If you thought writing apps using Rails was fast, well, you ain&#8217;t seen nothing til you&#8217;ve seen Ol&#8217; Blue Eyes take the stage.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.overwatering.org/blog/2009/03/find-mutual-follows-redux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Finding Mutual Follows</title>
		<link>http://www.overwatering.org/blog/2009/01/finding-mutual-follows/</link>
		<comments>http://www.overwatering.org/blog/2009/01/finding-mutual-follows/#comments</comments>
		<pubDate>Mon, 19 Jan 2009 10:52:00 +0000</pubDate>
		<dc:creator>giles</dc:creator>
				<category><![CDATA[blogging]]></category>
		<category><![CDATA[comp. sci.]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.overwatering.org/blog/2009/01/finding-mutual-follows/</guid>
		<description><![CDATA[When you&#8217;re a Twitter&#8217;er you will often be
in a situation where someone follows you, and you&#8217;re wondering, &#8216;Who
is this person? Do I know them?&#8217; Well, I can&#8217;t answer that question
for you. But, I have found that one thing that tells you about your
new follower is who they follow that you also follow. Follow?

I want to [...]]]></description>
			<content:encoded><![CDATA[<p>When you&#8217;re a <a href="http://twitter.com/gga">Twitter&#8217;er</a> you will often be
in a situation where someone follows you, and you&#8217;re wondering, &#8216;Who
is this person? Do I know them?&#8217; Well, I can&#8217;t answer that question
for you. But, I have found that one thing that tells you about your
new follower is who they follow that you also follow. Follow?</p>

<p>I want to be able to ask the question &#8216;Who do we know in common?&#8217;, in
short. A useful question, but one that can take quite a while to
answer using the web site. I
<a href="http://twitter.com/gga/status/1122570805">asked</a> the lazy
twitterverse if there was already an app for this, but my twitterverse is too small to get an answer. So, I
wrote my own script. I don&#8217;t have any handy web space to run this
from, so you&#8217;ll have to grab it and run it yourself. You will need to
install the <code>twitter4r</code> gem first:</p>

<p><pre><code>sudo gem install twitter4r
</code></pre></p>

<p>Then paste the following code into a Ruby file, and run. It takes two
parameters, the names of the two users for who you want to find common
ground.</p>

<p><pre><code>require 'rubygems'
require 'open-uri'
require 'rexml/document'
require 'twitter'<br/>
class Twitter::User<br />
  def all_friends
    users = friends.map { |f| f.screen_name }
    # If there's more than one page of users, we've already got the
    # first one
    page = 2
    found_users = friends.length
    while found_users &gt;= 100
      found_users = 0
      open("http://twitter.com/statuses/friends/#{screen_name}.xml?page=#{page}") do |f|
        users_doc = REXML::Document.new(f.readlines.join(''))
        users_doc.elements.each('/users/user/screen_name') do |friend_name|
          users &lt;&lt; friend_name.text
          found_users += 1
        end
      end
      page += 1
    end
    users
  end<br/>
end<br/>
def in_common(my_friends, other_friends)
  my_friends.select { |m_n| m_n if other_friends.member? m_n }
end<br/>
def main(me, other)
  c = Twitter::Client.new
  me_friends = c.user(me).all_friends
  other_friends = c.user(other).all_friends
  in_common(me_friends, other_friends).each do |f|
    puts "  #{f}"
  end
end<br/>
main(ARGV.shift, ARGV.shift)
</code></pre></p>

<p>Enjoy, and please let me know how it works out for you, or if you make
any changes. And by the way, *this* is why RESTful APIs rock.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.overwatering.org/blog/2009/01/finding-mutual-follows/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Automatic Deployment for Rails</title>
		<link>http://www.overwatering.org/blog/2008/12/automatic-deployment-for-rails/</link>
		<comments>http://www.overwatering.org/blog/2008/12/automatic-deployment-for-rails/#comments</comments>
		<pubDate>Sun, 07 Dec 2008 11:31:00 +0000</pubDate>
		<dc:creator>giles</dc:creator>
				<category><![CDATA[comp. sci.]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[recipe]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.overwatering.org/blog/2008/12/automatic-deployment-for-rails/</guid>
		<description><![CDATA[For the Rails applications we&#8217;re building at work, as well as all the
standard continuous integration features, we also automatically deploy
our applications. That is, every time we submit code a central server
is automatically updated with a new release. Before running tests.

We&#8217;re pretty happy with this set up. It&#8217;s already found a couple of
bugs in some plugins [...]]]></description>
			<content:encoded><![CDATA[<p>For the Rails applications we&#8217;re building at work, as well as all the
standard continuous integration features, we also automatically deploy
our applications. That is, every time we submit code a central server
is automatically updated with a new release. Before running tests.</p>

<p>We&#8217;re pretty happy with this set up. It&#8217;s already found a couple of
bugs in some plugins we&#8217;re using. More on that in an upcoming
post. Here&#8217;s how we made our automatic deployment work. We&#8217;re using
Capistrano for our deployment scripts, we&#8217;re deploying to Phusion
Passenger running under Apache on FreeBSD and our continuous
integration server runs an Ant script.</p>

<p>These instructions describe how to set up a Apache 2.2 web server with
Phusion Passenger on FreeBSD; the Ant script to automatically deploy
and how to configure a Rails app to be deployed like this.</p>

<p>This will give you two new environments for your apps: DEVTEST and
UAT. UAT is a user acceptance testing environment, our system testers
and analysts use and own this environment. We don&#8217;t automatically
deploy to here, we release to here. DEVTEST is the environment we
automatically deploy to.</p>

<h3>Setting up Your Server</h3>

<h4>Installing Phusion Passenger</h4>

<p>Installing Phusion Passenger on a FreeBSD server is no different to
installing anywhere else:</p>

<p><pre><code>$ sudo gem install passenger
$ sudo passenger-install-apache2-module
</code></pre></p>

<h4>Configuring Apache</h4>

<p>At the end of the second step, the installer tells you to add some
config to the end of your Apache config. On FreeBSD, edit this with:</p>

<p><pre><code>$ sudoedit /usr/local/etc/apache22/httpd.conf
</code></pre></p>

<p>And then add the following at the end:</p>

<p><pre><code>LoadModule passenger_module /usr/local/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so
PassengerRoot /usr/local/lib/ruby/gems/1.8/gems/passenger-2.0.3
PassengerRuby /usr/local/bin/ruby18
NameVirtualHost *:80
&lt;VirtualHost *:80&gt;
    ServerName devtest.example.com
    ServerAlias devtest
    DocumentRoot /usr/local/www/rails/devtest
    &lt;Directory "/usr/local/www/rails/devtest"&gt;
        Options FollowSymLinks
        AllowOverride None
        Order allow,deny
        Allow from all
    &lt;/Directory&gt;
    RailsEnv "devtest"
&lt;/VirtualHost&gt;
&lt;VirtualHost *:80&gt;
    ServerName uat.example.com
    ServerAlias uat
    DocumentRoot /usr/local/www/rails/uat
    &lt;Directory "/usr/local/www/rails/uat"&gt;
        Options FollowSymLinks
        AllowOverride None
        Order allow,deny
        Allow from all
    &lt;/Directory&gt;
    RailsEnv "uat"
&lt;/VirtualHost&gt;
</code></pre></p>

<p>Unless you want to use two different servers for the two environments,
you&#8217;ll need to use named virtual hosts, and ask your friendly
administrator to add CNAME records to your DNS server pointing devtest
and uat at the same physical server. They&#8217;ll know what you mean.</p>

<h4>Create a Local User</h4>

<p>You&#8217;ll need a local user on your server. This is the user that will
run the automatic deployments.</p>

<p><pre><code>$ sudo adduser
Username: deploy-robot
Full name: Deployment Robot
Uid (Leave empty for default):
Login group [deploy-robot]:
Login group is deploy-robot. Invite deploy-robot into other groups? []: www
Login class [default]:
Shell (sh csh tcsh zsh nologin) [sh]: 
Home directory [/home/deploy-robot]:
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]:
Use a random password? (yes/no) [no]:
Enter password:
Enter password again:
Lock out the account after creation? [no]:
Username   : deploy-robot
Password   : ****
Full Name  : Deployment Robot
Uid        : 1001
Class      :
Groups     : 
Home       : /home/deploy-robot
Shell      : /usr/local/bin/sh
Locked     : no
OK? (yes/no): yes
adduser: INFO: Successfully added (deploy-robot) to the user database.
Add another user? (yes/no): no
Goodbye!
</code></pre></p>

<h4>Deployment Directories</h4>

<p>Set up the directories to hold your applications.</p>

<p><pre><code>$ sudo mkdir -p /usr/local/www/rails/devtest
$ sudo mkdir -p /usr/local/www/rails/uat
</code></pre></p>

<p>These are the web roots for each of the environments, but applications
will not be deployed here. Instead, symlinks will be created from here
to where the applications are actually deployed.</p>

<p><pre><code>$ sudo mkdir -p /usr/local/app/rails/devtest
$ sudo mkdir -p /usr/local/app/rails/uat
</code></pre></p>

<p>These last two directories, and everything under them should be owned
by the deployment user you created above.</p>

<p><pre><code>$ sudo chown -R deploy-robot:www devtest uat
</code></pre></p>

<h4>Gems</h4>

<p>Finally, there are some gems you&#8217;ll need installed on the target
deployment server. Some of these depend on FreeBSD ports.</p>

<p><pre><code>$ cd /usr/ports/comms/ruby-termios
$ sudo make install clean
</code></pre></p>

<p>And then just a couple of gems.</p>

<p><pre><code>$ sudo gem install termios
$ sudo gem install capistrano
</code></pre></p>

<p>And that&#8217;s it for initial server configuration. There will be some
more configuration when first deploying an application.</p>

<h3>Preparing Your Application</h3>

<h4>Capistrano Config</h4>

<p>Capify your application:</p>

<p><pre><code>$ cd app
$ capify .
</code></pre></p>

<p>Edit your capistrano rules in <code>deploy.rb</code>. You&#8217;ll want them to look
something like the following. These rules use no source control
system to get the code. Our continuous integration server takes care
of checking out the code, so it&#8217;s easier to deploy from the local code
copy. And, this way we can be sure each deployment only contains one
changelist.</p>

<p><pre><code># Overall config
set :use_sudo, false
&#35; Application config
set :application, "app-name"
set :default_env, "production"
set :rails_env, ENV['RAILS_ENV'] || default_env
&#35; Deployment source and strategy
set :deploy_to, "/usr/local/app/rails/#{rails_env}/#{application}"
set :deploy_via, :copy
set :scm, :none
set :repository,  "."
&#35; Target servers
set :default_server, "localhost"
set :dest_server, ENV['SERVER'] || default_server
role :app, dest_server
role :web, dest_server
role :db,  dest_server, :primary =&gt; true
&#35; Phusion Passenger specific restart task
namespace :deploy do
    desc "Restart Application"
    task :restart, :roles =&gt; :app do
        run "touch #{current_path}/tmp/restart.txt"
    end
end
</code></pre></p>

<h4>Environment Configuration</h4>

<p>Set up the two new environments for your application.</p>

<p><pre><code>$ cp config/environments/production.rb config/environments/devtest.rb
$ cp config/environments/production.rb config/environments/uat.rb
</code></pre></p>

<p>Somewhere inside both those files you&#8217;ll need to set the
<code>RAILS_RELATIVE_URL_ROOT</code> as the application will be running at a
sub-URI on your server and Rails needs to know that. Something like:</p>

<p><pre><code>ENV['RAILS_RELATIVE_URL_ROOT'] = "/app-name"
</code></pre></p>

<p>The two new environments will also need to be described in your
<code>database.yml</code> file. This of course depends on your specific database
server setup, so I&#8217;ll leave that bit to you.</p>

<h4>Server-side Application Setup</h4>

<p>Apache needs to know about the applications, and there needs to be
symlinks from the web root to the application deployment folder. This
setup only needs to be done once for each application.</p>

<p>To add the application to Apache, edit
<code>/usr/local/etc/apache22/httpd.conf</code> again, and in the <code>VirtualHost</code>
section for the devtest environment, add a line like the following:</p>

<p><pre><code>RailsBaseURI /app-name
</code></pre></p>

<p>Now, set up the symlink:</p>

<p><pre><code>$ ln -s /usr/local/app/rails/devtest/app-name/current/public /usr/local/www/rails/devtest/app-name
</code></pre></p>

<p>And you&#8217;re done with the application configuration.</p>

<h3>Ant Deployment Scripts</h3>

<p>Our company has an in-house continuous integration server. We&#8217;d be too
embarrassed at cocktail parties if we didn&#8217;t have our own. Yes, yes, I
know this is completely ridiculous. And to make it even worse, it only
runs Ant scripts. Sigh. Anyway, here&#8217;s how you make Ant automatically
deploy an application to devtest.</p>

<p>In a file called <code>definitions.xml</code>:</p>

<p><pre><code>&lt;project name="definitions_rake"&gt;
    &lt;macrodef name="rake"&gt;
        &lt;attribute name="app" /&gt;
        &lt;attribute name="target" /&gt;
        &lt;element name="variables" optional="true" /&gt;
        &lt;sequential&gt;
            &lt;exec executable="rake" dir="@{app}" failonerror="true"&gt;
                &lt;arg value="@{target}" /&gt;
                &lt;variables /&gt;
            &lt;/exec&gt;
        &lt;/sequential&gt;
    &lt;/macrodef&gt;
    &lt;macrodef name="capistrano"&gt;
            &lt;attribute name="app" /&gt;
            &lt;attribute name="environment" /&gt;
            &lt;attribute name="task" /&gt;
            &lt;sequential&gt;
                &lt;exec executable="cap" dir="@{app}" failonerror="true"&gt;
                    &lt;env key="RAILS_ENV" value="@{environment}" /&gt;
                    &lt;env key="SERVER" value="${project.server}" /&gt;
                    &lt;arg value="@{task}" /&gt;
                    &lt;arg value="-s" /&gt;
                    &lt;arg value="user=${project.user}" /&gt;
                    &lt;arg value="-s" /&gt;
                    &lt;arg value="password=${project.password}" /&gt;
                &lt;/exec&gt;
            &lt;/sequential&gt;
    &lt;/macrodef&gt;
    &lt;macrodef name="deploy"&gt;
        &lt;attribute name="app" /&gt;
        &lt;attribute name="environment" /&gt;
        &lt;sequential&gt;
            &lt;capistrano app="@{app}" environment="@{environment}" task="deploy:setup" /&gt;
            &lt;capistrano app="@{app}" environment="@{environment}" task="deploy:migrations" /&gt;
        &lt;/sequential&gt;
    &lt;/macrodef&gt;
    &lt;macrodef name="test"&gt;
        &lt;attribute name="app" /&gt;
        &lt;sequential&gt;
            &lt;rake app="@{app}" target="db:migrate" /&gt;
            &lt;rake app="@{app}" target="test" /&gt;
            &lt;rake app="@{app}" target="spec" /&gt;
        &lt;/sequential&gt;
    &lt;/macrodef&gt;
&lt;/project&gt;
</code></pre></p>

<p>Ant macros, while quite insane, are generally a better way to define
new tasks than the complete insanity of trying to write a whole Ant
plugin in Java. These macros define low-level tasks to run <code>rake</code> and
<code>capistrano</code> tasks, and then use these to build up higher level tasks
like test and deploy. All these tasks assume that Ant has been run
from the directory immediately above your Rails app directory.</p>

<p>In a file called <code>project.properties</code>, set your server, user name and
password. Having the password here is unfortunate, but it is a local
account, with limited privileges on an internal server. Your call.</p>

<p><pre><code>user=deploy-robot
password=deploy-robot-password
server=deployment-server
</code></pre></p>

<p>In a file called <code>build.xml</code>:</p>

<p><pre><code>&lt;project name="aegean" default="build"&gt;
    &lt;import file="./definitions.xml" /&gt;
    &lt;property file="project.properties" prefix="project" /&gt;
    &lt;!-- Sample application.
         To add a new application:
         1. Copy the following targets.
         2. Replace 'depot' with your Rails app name.
         3. Add the 'app name' target as a dependency of the target 'build'.
    &lt;target name="depot.deploy.devtest"&gt;
           &lt;deploy app="depot" environment="devtest" /&gt;
    &lt;/target&gt;
    &lt;target name="depot.test"&gt;
           &lt;test app="depot" /&gt;
    &lt;/target&gt;
    &lt;target name="depot" depends="depot.deploy.devtest, depot.test" /&gt;
    --&gt;
    &lt;target name="example.deploy.devtest"&gt;
           &lt;deploy app="example" environment="devtest" /&gt;
    &lt;/target&gt;
    &lt;target name="example.test"&gt;
           &lt;test app="example" /&gt;
    &lt;/target&gt;
    &lt;target name="example" depends="example.deploy.devtest, example.test" /&gt;
    &lt;target name="build" depends="example" /&gt;
&lt;/project&gt;
</code></pre></p>

<p>The large comment block is just helpful for other developers trying to
add another application. From here, to try this out:</p>

<p><pre><code>$ ant
</code></pre></p>

<p>It should run the deployment, and then run the test suites. If that works as you expect, then just configure your continuous integration server to run Ant over that file on every submit.</p>

<p>Hopefully this is of use to someone. Though this is how our
environment is configured, I have written this all from memory, so I
might have missed a critical step somewhere. Please let me know if
there&#8217;s anything that needs to be changed.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.overwatering.org/blog/2008/12/automatic-deployment-for-rails/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

