Skip to content

Instantly share code, notes, and snippets.

@watersofoblivion
Created April 13, 2009 14:47
Show Gist options
  • Save watersofoblivion/94476 to your computer and use it in GitHub Desktop.
Save watersofoblivion/94476 to your computer and use it in GitHub Desktop.
My Generic Template for a Rails application
# Rails Application Template
#
# My personal template for Rails applications. Does a few things:
# - A few odds and ends (public/index.html, README => README.rdoc, etc.)
# - Sets up Rspec
# - Sets up Cucumber
# - Sets up Selenium
# - Stuffs everything into a git repository
# - Creates a project on Heroku and pushes the repo there
###
# Get some basic but important information from the user
###
heroku_name = ask "What Heroku name do you want for this application?"
###
# Clean up a bit
###
run "rm public/index.html"
run "rm README"
file "doc/README_FOR_APP", ""
file "config/template", "https://gist.github.com/94476.txt"
file "README.rdoc", <<EOF
= #{heroku_name}
About #{heroku_name}
== License
The MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
EOF
###
# Setup git
###
git :init
file '.gitignore', <<EOF
log/*.log
tmp/*
db/*.sqlite3
doc/app/*
coverage/*
config/database.yml
EOF
###
# Add a few plugins
###
plugin 'rspec', :git => 'git://github.com/dchelimsky/rspec.git'
plugin 'rspec-rails', :git => 'git://github.com/dchelimsky/rspec-rails.git'
plugin 'selenium-on-rails', :git => 'git://github.com/paytonrules/selenium-on-rails.git'
###
# Setup the testing environment
#
# This entails generating a bunch of files...
###
run "rm -rf test/"
# Do the initial Rspec and Cucumber setup
generate 'rspec'
generate 'cucumber'
run "mkdir features/regular features/ajax"
# A helper is always, well, helpful...
file 'features/support/helper.rb', ""
# A slight modifications of the Webrat code to work with Selenium
file 'features/step_definitions/selenium_steps.rb', <<EOF
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
# Commonly used webrat steps modified to work with Selenium
Given /^I am on (.+)$/ do |page_name|
visit path_to(page_name)
end
When /^I go to (.+)$/ do |page_name|
visit path_to(page_name)
selenium.wait_for_page_to_load
end
When /^I press "([^\"]*)"$/ do |button|
click_button(button)
selenium.wait_for_page_to_load
end
When /^I follow "([^\"]*)"$/ do |link|
click_link(link)
selenium.wait_for_page_to_load
end
When /^I fill in "([^\"]*)" with "([^\"]*)"$/ do |field, value|
fill_in(field, :with => value)
end
When /^I select "([^\"]*)" from "([^\"]*)"$/ do |value, field|
select(value, :from => field)
end
# Use this step in conjunction with Rail's datetime_select helper. For example:
# When I select "December 25, 2008 10:00" as the date and time
When /^I select "([^\"]*)" as the date and time$/ do |time|
select_datetime(time)
end
# Use this step when using multiple datetime_select helpers on a page or
# you want to specify which datetime to select. Given the following view:
# <%= f.label :preferred %><br />
# <%= f.datetime_select :preferred %>
# <%= f.label :alternative %><br />
# <%= f.datetime_select :alternative %>
# The following steps would fill out the form:
# When I select "November 23, 2004 11:20" as the "Preferred" data and time
# And I select "November 25, 2004 10:30" as the "Alternative" data and time
When /^I select "([^\"]*)" as the "([^\"]*)" date and time$/ do |datetime, datetime_label|
select_datetime(datetime, :from => datetime_label)
end
# Use this step in conjunction with Rail's time_select helper. For example:
# When I select "2:20PM" as the time
# Note: Rail's default time helper provides 24-hour time-- not 12 hour time. Webrat
# will convert the 2:20PM to 14:20 and then select it.
When /^I select "([^\"]*)" as the time$/ do |time|
select_time(time)
end
# Use this step when using multiple time_select helpers on a page or you want to
# specify the name of the time on the form. For example:
# When I select "7:30AM" as the "Gym" time
When /^I select "([^\"]*)" as the "([^\"]*)" time$/ do |time, time_label|
select_time(time, :from => time_label)
end
# Use this step in conjunction with Rail's date_select helper. For example:
# When I select "February 20, 1981" as the date
When /^I select "([^\"]*)" as the date$/ do |date|
select_date(date)
end
# Use this step when using multiple date_select helpers on one page or
# you want to specify the name of the date on the form. For example:
# When I select "April 26, 1982" as the "Date of Birth" date
When /^I select "([^\"]*)" as the "([^\"]*)" date$/ do |date, date_label|
select_date(date, :from => date_label)
end
When /^I check "([^\"]*)"$/ do |field|
check(field)
end
When /^I uncheck "([^\"]*)"$/ do |field|
uncheck(field)
end
When /^I choose "([^\"]*)"$/ do |field|
choose(field)
end
When /^I attach the file at "([^\"]*)" to "([^\"]*)"$/ do |path, field|
attach_file(field, path)
end
Then /^I should see "([^\"]*)"$/ do |text|
response.should contain(text)
end
Then /^I should not see "([^\"]*)"$/ do |text|
response.should_not contain(text)
end
Then /^the "([^\"]*)" field should contain "([^\"]*)"$/ do |field, value|
field_labeled(field).value.should =~ /\#{value}/
end
Then /^the "([^\"]*)" field should not contain "([^\"]*)"$/ do |field, value|
field_labeled(field).value.should_not =~ /\#{value}/
end
Then /^the "([^\"]*)" checkbox should be checked$/ do |label|
field_labeled(label).should be_checked
end
Then /^I should be on (.+)$/ do |page_name|
URI.parse(selenium.get_location).path.should == path_to(page_name)
end
EOF
# Modify the Cucumber rake task to use the default profile
file 'lib/tasks/cucumber.rake', <<EOF
$LOAD_PATH.unshift(RAILS_ROOT + '/vendor/plugins/cucumber/lib') if File.directory?(RAILS_ROOT + '/vendor/plugins/cucumber/lib')
begin
require 'cucumber/rake/task'
Cucumber::Rake::Task.new(:features) do |t|
t.profile = "default"
t.cucumber_opts = "--format pretty"
end
task :features => 'db:test:prepare'
rescue LoadError
desc 'Cucumber rake task not available'
task :features do
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
end
end
EOF
# Create a selenium rake file which uses the ajax profile
file 'lib/tasks/selenium.rake', <<EOF
$LOAD_PATH.unshift(RAILS_ROOT + '/vendor/plugins/cucumber/lib') if File.directory?(RAILS_ROOT + '/vendor/plugins/cucumber/lib')
begin
require 'cucumber/rake/task'
Cucumber::Rake::Task.new(:selenium) do |t|
t.profile = "ajax"
t.cucumber_opts = "--format pretty"
end
task :features => 'db:test:prepare'
rescue LoadError
desc 'Cucumber rake task not available'
task :features do
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
end
end
EOF
# The shared environment
file "features/support/env.rb", <<EOF
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
require 'cucumber/rails/world'
require 'cucumber/formatters/unicode'
require 'webrat'
EOF
# The webrat environment
file "features/support/regular.rb", <<EOF
require 'spec/expectations'
require 'webrat'
Webrat.configure do |config|
config.mode = :rails
end
require 'database_cleaner'
require 'database_cleaner/cucumber'
DatabaseCleaner.clean_with :truncation
DatabaseCleaner.strategy = :transaction
EOF
# The selenium environment
file "features/support/ajax.rb", <<EOF
require 'spec/expectations'
require 'selenium'
require 'webrat'
Webrat.configure do |config|
config.mode = :selenium
config.application_environment = :test
end
require 'database_cleaner'
require 'database_cleaner/cucumber'
DatabaseCleaner.strategy = :truncation
EOF
# Configure the Cucumber profiles
file 'cucumber.yml', <<EOF
default: --require features/support/env.rb --require features/support/regular.rb --require features/support/helper.rb --require features/step_definitions/webrat_steps.rb features/regular
ajax: --require features/support/env.rb --require features/support/ajax.rb --require features/support/helper.rb --require features/step_definitions/selenium_steps.rb features/regular features/ajax
EOF
# An end to end testing Rake task. NOT IMPLEMENTED COMPLETELY (YET...)
rakefile "end_to_end.rake" do
<<EOF
# Run complete End-to-End testing in a test production environment
#
# I. Setup the tests
# 1. Clone the application repository into a temporary directory
# 2. Deploy the app to a new Heroku application
# 3. Copy the current production database to the testing app (via TAPS)
# 4. Run the pending migrations
# II. Run the tests
# 1. Run the specs
# 2. Run the features
# 3. Run the selenium tests
# III. Tear down the tests
# 1. Undeploy the application from Heroku
# 2. Delete the cloned repository
# Some helpful constants
PRODUCTION_APP = "#{heroku_name}"
CLONED_REPO = "\#{RAILS_ROOT}/tmp/e2et"
TESTING_APP = "\#{PRODUCTION_APP}-e2et"
namespace :test do
desc "Run complete end-to-end testing"
task :end_to_end => ["end_to_end:setup", "end_to_end:test", "end_to_end:teardown"]
namespace :end_to_end do
# Set up the testing environment
task :setup => ["setup:clone", "setup:deploy", "setup:copy", "setup:migrate"]
namespace :setup do
# Clone the application repository into a separate directory
task :clone do |t|
sh "git clone \#{RAILS_ROOT} \#{CLONED_REPO}"
end
# Deploy the cloned repository to a new Heroku application
task :deploy => :clone do |t|
FileUtils.cd CLONED_REPO do
sh "heroku create \#{TESTING_APP}"
sh "git push heroku master"
end
end
# Copy the current production database to the new Heroku application
task :copy => :deploy do |t|
sh "heroku db:pull"
sh "mkdir -p \#{CLONED_REPO}/db/"
sh "cp \#{RAILS_ROOT}/config/database.yml \#{CLONED_REPO}/config/database.yml"
sh "cp \#{RAILS_ROOT}/db/development.sqlite3 \#{CLONED_REPO}/db/"
FileUtils.cd CLONED_REPO do
sh "heroku db:push"
end
end
# Run the pending database migrations
task :migrate => :copy do |t|
FileUtils.cd CLONED_REPO do
sh "heroku rake db:migrate"
end
end
end
# Run all of the tests in the production environment
task :test => [:setup, "test:spec", "test:features", "test:selenium"]
namespace :test do
# Run the specs
task :spec do |t|
FileUtils.cd CLONED_REPO do
sh "heroku rake spec"
end
end
# Run the features
task :features do |t|
FileUtils.cd CLONED_REPO do
sh "heroku rake features"
end
end
# Run selenium
task :selenium do |t|
# TODO: Kick off selenium-grid
FileUtils.cd CLONED_REPO do
sh "rake test:acceptance"
end
end
end
# Tear down the testing environment
task :teardown => [:test, "teardown:undeploy", "teardown:unclone"]
namespace :teardown do
# Remove the application from Heroku
task :undeploy do |t|
FileUtils.cd CLONED_REPO do
sh "echo 'y' | heroku destroy --app \#{TESTING_APP}"
end
end
# Delete the cloned repository
task :unclone => :undeploy do |t|
sh "rm -rf \#{CLONED_REPO}"
end
end
end
end
EOF
end
###
# Add everything and commit it to a repository
###
git :add => "."
git :commit => "-a -m 'Initial import'"
###
# Create a Heroku app
###
run "heroku create #{heroku_name}"
git :push => "heroku master"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment