Thursday, October 05, 2006

Ruby Project Tree

I was recently asked: How would you structure a (non-Rails) Ruby project?

Here's a list of the things I generally do when creating a new project:
  • Create lib and test folders to house the code and tests respectively.
  • Create a rakefile.rb with a task for running all the tests:
    task :default => :test

    task :test do
    require File.dirname(__FILE__) + '/test/all_tests.rb'
    end
  • Create a test file that runs all tests (all_tests.rb):
    Dir['**/*_test.rb'].each { |test_case| require test_case }
  • Create a test_helper file that handles common require calls:
    require 'test/unit'
    require 'mocha'
    require 'stubba'
    require File.dirname(__FILE__) + '/../lib/[project name]'
  • Create a file with the same name as my project in the lib folder. This file contains all the requires necessary to run the application/use the library:
    [example: sqldsl.rb]

    lib = File.dirname(__FILE__)

    require lib + '/object.rb'
    require lib + '/symbol.rb'
    require lib + '/array.rb'
    require lib + '/string.rb'
    require lib + '/numeric.rb'
    require lib + '/time.rb'
    require lib + '/sql_statement.rb'
    require lib + '/where_builder.rb'
    require lib + '/select.rb'
    require lib + '/insert.rb'
    require lib + '/update.rb'
    require lib + '/delete.rb'
I find this structure makes my life easier.

Of course, if you want this automated, you could always just use tree_surgeon.rb
require 'fileutils'

project_name = ARGV[0]
lib = project_name + "/lib"
test = project_name + "/test"
rakefile = project_name + '/rakefile.rb'
all_tests = test + "/all_tests.rb"
main = project_name + "/lib/" + project_name + ".rb"
test_helper = test + "/test_helper.rb"

FileUtils.rm_rf project_name

puts "creating: " + project_name
Dir.mkdir project_name

puts "creating: " + lib
Dir.mkdir lib

puts "creating: " + test
Dir.mkdir test

puts "creating: " + rakefile
File.open(rakefile, 'w') do |file|
file << <<-eos
task :default => :test

task :test do
require File.dirname(__FILE__) + '/test/all_tests.rb'
end
eos
end

puts "creating: " + all_tests
File.open(all_tests, 'w') do |file|
file << "Dir['**/*_test.rb'].each { |test_case| require test_case }"
end

puts "creating: " + main
FileUtils.touch main

puts "creating: " + test_helper
File.open(test_helper, 'w') do |file|
file << <<-eos
require 'test/unit'
require File.dirname(__FILE__) + '/../lib/#{project_name}'
eos
end
Also, if you are creating a RubyGem it is recommended that you create README, TODO, and CHANGES files. Additionally, if your gem uses executables it is suggested that you store the executables in a bin folder and create a install.rb file that calls your executables.

Do you have other suggestions?
Post a Comment