It hurts, but this article is now obsolete. Rails 2.3 has templates, there are a variety of starter applications, and the text of this article was reformatted and put into Deploying Rails Applications. Good bye, article.

Pinkatio – our latest Rails application

We just had a killer idea to get us thousands of dollars of revenue per month. We will call it pinkatio, and we will put it on Rails.

Creating the Subversion repository

This is the most variable step in the whole procedure. If you know your way around a Unix/Linux system, go ahead and put the repository in /var. Watch your permissions, though. Else, I suggest sticking with a HOME based repository, like I did below. You can use the file repository access method. The only caveat is that you will not be able to share your repository with other people using that method.

To create the repository, we simply call svnadmin’s create subcommand:

1 $ mkdir ~/svn
2 $ svnadmin create --fs-type=fsfs ~/svn/pinkatio

To ease our poor fingers, let us create an environment variable to refer tot he repository’s root URL:

1 $ REPOS=file://`pwd`/svn/pinkatio

Subversion recommends creating all repositories with three folders at the root: trunk, tags and branches. This is if you use the one project per repository. This is explained in more details in Choosing a Repository Layout.

This is by no means a requirement to use Subversion, but I suggest sticking to this convention anyway. Most Subversion repositories I have seen adhere to the convention, and if you have only one project in your repository, it makes sense to be able to tag and branch at will.

1 $ svn mkdir --message="Initial project layout" $REPOS/trunk $REPOS/tags $REPOS/branches
2 
3 Committed revision 1.

With the repository creation out of the way, let us now turn to creating our Rails application.

Creating the Rails application and importing into the repository

Creating the Rails application is straightforward:

1 $ rails ~/pinkatio
2       create
3       create  app/controllers
4       ...
5 $ cd ~/pinkatio

At this point, you could do an svn import and import the whole application into Subversion. I recommend against doing that. If you use the “in-place import” procedure, you can commit only the pieces that you want, not the whole tree (log files being ones we don’t want under version control). See Subversion’s How can I do an in-place ‘import’ FAQ for the full details.

1 $ svn checkout $REPOS/trunk .
2 Checked out revision 1.

Next, let us add the whole tree to the working copy. This is no different than if we had done an svn import initially, except all changes are local, and we can selectively revert files and folders.

1 $ svn add --force .
2 A         app
3 ...
4 A         README

The Rails command helpfully creates most of the tree. Since I use migrations in all of my Rails projects, I immediately create the db/migrate/ folder. Edge Rails and Rails 1.1 also include a tmp/ folder. For completeness’ sake, I create it at the same time.

1 $ svn mkdir db/migrate tmp 
2 A         db/migrate
3 A         tmp

Removing the log files from version control

Right now, Subversion will helpfully track changes to the log files. This is not really useful for us, as the log files can be pruned at any point.

To ease our burden, the easiest thing is to tell Subversion to ignore the logs.

1 $ svn revert log/*
2 Reverted 'log/development.log'
3 Reverted 'log/production.log'
4 Reverted 'log/server.log'
5 Reverted 'log/test.log'
6 
7 $ svn propset svn:ignore "*.log" log
8 property 'svn:ignore' set on 'log'

See svn:ignore in the Subversion book for more details on the property format.

Managing the database configuration

Again the Subversion FAQ comes to the rescue: I have a file in my project that every developer must change, but I don’t want those local mods to ever be committed. How can I make ‘svn commit’ ignore the file?.

The solution is to have a template of the file in the repository, and to force each working copy to copy the template file to the real file. Let us simply revert the add of the config/database.yml file, and add a sample file instead:

 1 $ svn revert config/database.yml
 2 Reverted 'config/database.yml'
 3 
 4 $ mv config/database.yml config/database.yml.sample
 5 $ svn add config/database.yml.sample
 6 A         config/database.yml.sample
 7 
 8 $ svn propset svn:ignore "database.yml" config
 9 property 'svn:ignore' set on 'config'
10 $ cp config/database.yml.sample config/database.yml

The only problem with this procedure is if important changes are made to the config.yml.sample file, the developers might not notice the changes. Most of the time though, the sample file will not change, and leaving it as-is is ok.

Database structure dumps during testing

When you run the tests, Rails dumps the development database’s structure to a file in db/. Usually, this file should not be under version control. Your migration scripts should be under version control instead, and your migrations should enable you to recreate the development database at will.

Additionally, this step will depend on which configuration setting you use for the config.active_record.schema_format. If you use the :ruby (the default on Edge Rails and Rails 1.1), you should ignore the schema.rb file from db/. If you use :sql, simply ignore development_structure.sql instead. Alternatively, you could ignore both files, making this a moot point.

1 $ svn propset svn:ignore "schema.rb" db
2 property 'svn:ignore' set on 'db'

tmp/, documentation, scripts and public

Edge Rails and Rails 1.1 now possess a tmp/ folder. Since this folder will hold socket and session files, we can safely ignore everything in it.

1 $ svn propset svn:ignore "*" tmp
2 property 'svn:ignore' set on 'tmp'

The doc/ folder can hold two subfolders: appdoc/ and apidoc/. If you don’t plan on building the documentation for your project, you can ignore setting svn:ignore on doc/. Else, you should ignore like this:

1 $ svn propset svn:ignore "*doc" doc
2 property 'svn:ignore' set on 'doc'

Subversion also has a property that tells it which files are executable. We can set the property on files that are intended to be run from the command line:

1 $ svn propset svn:executable "*" `find script -type f | grep -v '.svn'` public/dispatch.*
2 property 'svn:executable' set on 'script/performance/benchmarker'
3 ...

Last but not least, my projects usually have a default home page served by a Rails action. This means building a route and removing public/index.html:

1 $ svn revert public/index.html
2 Reverted 'public/index.html'
3 
4 $ rm public/index.html

Saving our work

After all of these changes, it is important to commit our work to the repository.

1 $ svn commit --message="New Rails project"
2 Adding         README
3 ...
4 Adding         vendor/plugins
5 Transmitting file data .......................................
6 Committed revision 2.

After this step, it is time to start coding your application, unless you need to go on the Edge…

Using Rails Edge and protecting against overzealous gem upgrades

When you are going to put your application into production, you don’t want an upgrade in your host’s environment to affect your application. To prevent such problems, you should keep a local copy of Rails in your application’s vendor folder.

If you want to live on the Edge (with all the latest features), this step is a necessity. If you are not so comfortable with Edge, replace trunk/ with tags/rel_1-0-0 (or tags/rel_1-1-0 when Rails 1.1 is out) in the svn:externals property below.

 1 $ svn propset svn:externals "rails http://dev.rubyonrails.org/svn/rails/trunk/" vendor
 2 property 'svn:externals' set on 'vendor'
 3 
 4 $ svn update vendor
 5 
 6 Fetching external item into 'vendor/rails'
 7 A    vendor/rails/cleanlogs.sh
 8 ...
 9  U   vendor/rails
10 Updated external to revision 3830.
11 
12 Updated to revision 2.

If you went for Rails Edge, you should really rerun the rails . command after you update. This will ensure you get the latest version of the scripts and javascript files. And since we have not made any changes to the contents of any files, now is the best time to do this.

1 $ yes | rails .
2       exists
3       exists  app/controllers
4 ...
5    identical  log/test.log

Don’t forget to commit your changes:

1 $ svn commit --message="Living on the Edge - set svn:externals on vendor/ for Rails"
2 Sending        vendor
3 
4 Committed revision 3.

Tracking Edge Rails

Next time you svn update, Subversion will go out to the Rails repository and retrieve all changes since your last checkout. If the JavaScript files changed, you should copy them over using the rails:update Rake command:

 1 $ svn update
 2 ...
 3 Updated external to revision 3831.
 4 
 5 Updated to revision 2.
 6 
 7 $ rake rails:update
 8 (in /home/fbos/pinkachio)
 9 
10 $ svn status
11 M      public/javascripts/prototype.js
12 M      public/javascripts/effects.js
13 M      public/javascripts/dragdrop.js
14 M      public/javascripts/controls.js
15 
16 $ svn commit --message="Updated JavaScripts to latest revision"
17 M      public/javascripts/prototype.js
18 ...

Gems

Gem features a useful subcommand: unpack. When you run it on a gem, it will unpack that gem’s content into the current folder. We can then move the code to our vendor/ folder, and again protect ourselves against host upgrades.

As an example, let us unpack the Money gem:

1 $ cd vendor
2 $ gem unpack money
3 Unpacked gem: 'money-1.7.1'

Gems all have a lib/ folder into which the gem’s source code is stored. Copying the contents of the lib/ folder into vendor/ is the important trick here. If you move lib/ to vendor, it won’t help, as Rails automatic dependency loading mechanism will not know how to find your code.

At the same time, to comply with the library’s license, we copy the library verbatim to our vendor/ folder.

1 $ cp -Rf money-1.7.1/lib/* .
2 $ cp -Rf money-1.7.1/MIT-LICENSE LICENSE-money
3 $ cp -Rf money-1.7.1/README README-money

Let us tell Subversion what files we now want to version control:

1 $ svn add bank money support money.rb LICENSE-money README-money
2 A         bank
3 ..
4 A         README-money

To help me remember which version I unpacked, I set a custom property on the main file of the library I just unpacked:

1 $ svn propset version "1.7.1 (Gem)" money.rb
2 property 'version' set on 'money.rb'

Next, we cleanup after ourselves:

1 $ rm -Rf money-1.7.1

Finally, let us commit our changes back to the repository.

1 $ cd ..
2 $ svn commit --message="Unpacked Money 1.7.1 into vendor/"
3 Adding         vendor/LICENSE-money
4 ...
5 Transmitting file data ........
6 Committed revision 4.

Gem upgrades

When the next version of the Money gem will come around, we simply follow the same procedure as above. Of course, you will use svn status to know what files changed exactly. You might have to add new files, and remove old ones.

One tool that can help automate this process svn_load_dirs.pl, from the Subversion’s contrib/ area.

Plugins

For plugins, you have to take the same decision as for Rails – Edge or safe. For plugins, I have found that sticking to released version is safer for me. YMMV.

As an example, I will use the FileColumn plugin. Unfortunately, this plugin is not ready to be used by the script/plugin install -x procedure. So, we have to resort to a manual one.

 1 $ svn propset svn:externals "file_column http://opensvn.csie.org/rails_file_column/plugins/file_column/tags/rel_0-3-1/" vendor/plugins
 2 property 'svn:externals' set on 'vendor/plugins'
 3 
 4 $ svn update vendor/plugins
 5 
 6 Fetching external item into 'vendor/plugins/file_column'
 7 A    vendor/plugins/file_column/test
 8 ...
 9  U   vendor/plugins/file_column
10 Updated external to revision 58.
11 
12 Updated to revision 4.

Again, we must not forget to commit our changes to the repository:

1 $ svn commit --message="Added FileColumn plugin"
2 Sending        vendor/plugins
3 
4 Committed revision 5.

Creating migrations, models and controllers

The Rails generate has a helpful option: --svn (-c):

1 $ script/generate migration --svn InitialSchema
2       exists  db/migrate
3       create  db/migrate/001_initial_schema.rb
4 A         db/migrate/001_initial_schema.rb
5 
6 $ svn status
7 A      db/migrate/001_initial_schema.rb

All generate and destroy generators accept the --svn option. This makes it easy for the developer to keep his changes under version control.

Final words

I follow this script more or less verbatim for all of my Rails projects. After the first two or three times, this becomes automatic. For the adventurous, I have a shell script which does most of the steps above automatically. You can get rails2.sh.

rails2 license:

# Distributed in the Public Domain by Francois Beausoleil.
# This script might destroy twenty years worth of work, and I cannot be held
# responsible.  You are your own master.  Read this file in detail before
# you use it.
#
# NO IMPLIED WARRANTY.

Search

Your Host

A picture of me

I am François Beausoleil, a Ruby on Rails and Scala developer. During the day, I work on Seevibes, a platform to measure social interactions related to TV shows. At night, I am interested many things. Read my biography.

Top Tags

Books I read and recommend

Links

Projects I work on

Projects I worked on