rails -v
Rails 2.3.2
ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
gem -v
1.3.3
Our store resells products, so lets start with a list of products provided by our supplier. We are ultimately going to want to do things with these products, so lets load them into a database.
Start with some XML, listing a number of products.
edit testdata.xml
<?xml version="1.0" encoding="UTF-8"?>
<products type="array">
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
<em>Pragmatic Project Automation</em> shows you how to improve the
consistency and repeatability of your project's procedures using
automation to reduce risk and errors.
</p>
<p>
Simply put, we're going to put this thing called a computer to work
for you doing the mundane (but important) project stuff. That means
you'll have more time and energy to do the really
exciting---and difficult---stuff, like writing quality code.
</p></description>
<id type="integer">2</id>
<image-url>/images/auto.jpg</image-url>
<price type="decimal">24.95</price>
<title>Pragmatic Project Automation</title>
<updated-at type="datetime">2009-07-05T16:32:09Z</updated-at>
</product>
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
This book is a recipe-based approach to using Subversion that will
get you up and running quickly---and correctly. All projects need
version control: it's a foundational piece of any project's
infrastructure. Yet half of all project teams in the U.S. don't use
any version control at all. Many others don't use it well, and end
up experiencing time-consuming problems.
</p></description>
<id type="integer">3</id>
<image-url>/images/svn.jpg</image-url>
<price type="decimal">28.5</price>
<title>Pragmatic Version Control</title>
<updated-at type="datetime">2009-07-05T16:26:31Z</updated-at>
</product>
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
Pragmatic programmers use feedback to drive their development and
personal processes. The most valuable feedback you can get while
coding comes from unit testing.
</p>
<p>
Without good tests in place, coding can become a frustrating game of
"whack-a-mole." That's the carnival game where the player strikes at a
mechanical mole; it retreats and another mole pops up on the opposite side
of the field. The moles pop up and down so fast that you end up flailing
your mallet helplessly as the moles continue to pop up where you least
expect them.
</p></description>
<id type="integer">4</id>
<image-url>/images/utc.jpg</image-url>
<price type="decimal">27.75</price>
<title>Pragmatic Unit Testing (C#)</title>
<updated-at type="datetime">2009-07-05T16:26:31Z</updated-at>
</product>
</products>
Test that loading the XML produces the right data in the database.
edit test_products.rb
require 'test/unit'
class Product_Load_Test < Test::Unit::TestCase
def setup
@db = `sqlite3 -line products.db 'select * from products'`
end
def test_automate
assert_match /title = Pragmatic Project Automation/, @db
assert_match /description = <p>\s+<em>Pragmatic Project Automation/, @db
assert_match %r|image_url = /images/auto.jpg|, @db
assert_match /price = 24.95/, @db
end
def test_version
assert_match /title = Pragmatic Version Control/, @db
assert_match /description = <p>\s+This book is a recipe-based approach/, @db
assert_match %r|image_url = /images/svn.jpg|, @db
assert_match /price = 28.5/, @db
end
def test_unit
assert_match /title = Pragmatic Unit Testing/, @db
assert_match /description = <p>\s+Pragmatic programmers use feedback/, @db
assert_match %r|image_url = /images/utc.jpg|, @db
assert_match /price = 27.75/, @db
end
end
Run that test, watch it fail.
ruby test_products.rb
Loaded suite test_products
Started
SQL error: no such table: products
SQL error: no such table: products
SQL error: no such table: products
FFF
Finished in 0.052071 seconds.
1) Failure:
test_automate(Product_Load_Test) [test_products.rb:9]:
<""> expected to be =~
</title = Pragmatic Project Automation/>.
2) Failure:
test_unit(Product_Load_Test) [test_products.rb:23]:
<""> expected to be =~
</title = Pragmatic Unit Testing/>.
3) Failure:
test_version(Product_Load_Test) [test_products.rb:16]:
<""> expected to be =~
</title = Pragmatic Version Control/>.
3 tests, 3 assertions, 3 failures, 0 errors
Now write the code that loads the database from XML.
edit load_products.rb
require 'rexml/document'
require 'sqlite3'
input = File.new('testdata.xml')
output = SQLite3::Database.new('products.db')
output.execute_batch <<SQL
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
title varchar(255),
description text,
image_url varchar(255),
price decimal(8,2) DEFAULT 0
);
SQL
REXML::Document.new(input).each_element('//product') do |product|
output.execute 'INSERT INTO products(title,description,image_url,price)' +
' VALUES (?, ?, ?, ?)',
product.elements['title'].text,
product.elements['description'].text,
product.elements['image-url'].text,
product.elements['price'].text
end
input.close
output.close
Verify that the this code does what it is intended to do.
ruby load_products.rb
ruby test_products.rb
Loaded suite test_products
Started
...
Finished in 0.010379 seconds.
3 tests, 12 assertions, 0 failures, 0 errors
Try it a second time -- see a problem.
ruby load_products.rb
/usr/lib/ruby/1.8/sqlite3/errors.rb:62:in `check': table products already exists (SQLite3::SQLException)
from /usr/lib/ruby/1.8/sqlite3/statement.rb:39:in `initialize'
from /usr/lib/ruby/1.8/sqlite3/database.rb:154:in `new'
from /usr/lib/ruby/1.8/sqlite3/database.rb:154:in `prepare'
from /usr/lib/ruby/1.8/sqlite3/database.rb:225:in `execute_batch'
from load_products.rb:7
Before proceeding, set up git.
cat /home/rubys/.gitconfig
[user]
name = Sam Ruby
email = rubys@intertwingly.net
[alias]
st = status
Verify the configuration.
git repo-config --get-regexp user.*
user.name Sam Ruby
user.email rubys@intertwingly.net
Initialize a repository for the code.
git init
Initialized empty Git repository in /home/rubys/git/awdwr/work/depot/.git/
Add everything in the current directory.
git add .
Commit the changes.
git commit -m "load via raw SQLite3"
Created initial commit a679b97: load via raw SQLite3
4 files changed, 114 insertions(+), 0 deletions(-)
create mode 100644 load_products.rb
create mode 100644 products.db
create mode 100644 test_products.rb
create mode 100644 testdata.xml
At this point, we could simply just add a DROP TABLE IF EXISTS to the SQL, but that's a wee bit drastic. Over time, we are going to want to add columns (e.g., quantity_on_hand), so lets match products in the database against the contents of the XML by their original ("base") id, and update existing rows if they are already present, adding new rows when they are not.
Conditionally CREATE table, match based on id, and UPDATE when found.
edit load_products.rb
require 'rexml/document'
require 'sqlite3'
input = File.new('testdata.xml')
output = SQLite3::Database.new('products.db')
unless output.execute('select name from sqlite_master').include? ["products"]
output.execute_batch <<-SQL
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
base_id INTEGER,
title varchar(255),
description text,
image_url varchar(255),
price decimal(8,2) DEFAULT 0
);
SQL
end
REXML::Document.new(input).each_element('//product') do |product|
title = product.elements['title'].text
description = product.elements['description'].text
image_url = product.elements['image-url'].text
price = product.elements['price'].text
base_id = product.elements['id'].text
id = output.execute('SELECT id FROM products WHERE base_id=?', base_id)
if id.empty?
output.execute 'INSERT INTO products' +
'(base_id, title, description, image_url, price)' +
' VALUES (?, ?, ?, ?, ?)', base_id, title, description, image_url, price
else
output.execute 'UPDATE products ' +
'SET base_id=?, title=?, description=?, image_url=?, price=? WHERE id=?',
base_id, title, description, image_url, price, id.first
end
end
input.close
output.close
Run the same test as before.
ruby load_products.rb
/usr/lib/ruby/1.8/sqlite3/errors.rb:62:in `check': no such column: base_id (SQLite3::SQLException)
from /usr/lib/ruby/1.8/sqlite3/statement.rb:39:in `initialize'
from /usr/lib/ruby/1.8/sqlite3/database.rb:154:in `new'
from /usr/lib/ruby/1.8/sqlite3/database.rb:154:in `prepare'
from /usr/lib/ruby/1.8/sqlite3/database.rb:181:in `execute'
from load_products.rb:28
from /usr/lib/ruby/1.8/rexml/element.rb:892:in `each'
from /usr/lib/ruby/1.8/rexml/xpath.rb:53:in `each'
from /usr/lib/ruby/1.8/rexml/element.rb:892:in `each'
from /usr/lib/ruby/1.8/rexml/element.rb:393:in `each_element'
from load_products.rb:20
Note a problem. For now, simply delete the database and try again.
rm products.db
ruby load_products.rb
ruby test_products.rb
Loaded suite test_products
Started
...
Finished in 0.018474 seconds.
3 tests, 12 assertions, 0 failures, 0 errors
Try again.
ruby load_products.rb
ruby test_products.rb
Loaded suite test_products
Started
...
Finished in 0.01182 seconds.
3 tests, 12 assertions, 0 failures, 0 errors
See what files have changed.
git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: load_products.rb
# modified: products.db
#
no changes added to commit (use "git add" and/or "git commit -a")
See what the changes were.
git diff
diff --git a/load_products.rb b/load_products.rb
index 83e8720..c9568b6 100644
--- a/load_products.rb
+++ b/load_products.rb
@@ -4,23 +4,37 @@ require 'sqlite3'
input = File.new('testdata.xml')
output = SQLite3::Database.new('products.db')
-output.execute_batch <<SQL
- CREATE TABLE products (
- id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
- title varchar(255),
- description text,
- image_url varchar(255),
- price decimal(8,2) DEFAULT 0
- );
-SQL
+unless output.execute('select name from sqlite_master').include? ["products"]
+ output.execute_batch <<-SQL
+ CREATE TABLE products (
+ id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ base_id INTEGER,
+ title varchar(255),
+ description text,
+ image_url varchar(255),
+ price decimal(8,2) DEFAULT 0
+ );
+ SQL
+end
REXML::Document.new(input).each_element('//product') do |product|
- output.execute 'INSERT INTO products(title,description,image_url,price)' +
- ' VALUES (?, ?, ?, ?)',
- product.elements['title'].text,
- product.elements['description'].text,
- product.elements['image-url'].text,
- product.elements['price'].text
+ title = product.elements['title'].text
+ description = product.elements['description'].text
+ image_url = product.elements['image-url'].text
+ price = product.elements['price'].text
+
+ base_id = product.elements['id'].text
+
+ id = output.execute('SELECT id FROM products WHERE base_id=?', base_id)
+ if id.empty?
+ output.execute 'INSERT INTO products' +
+ '(base_id, title, description, image_url, price)' +
+ ' VALUES (?, ?, ?, ?, ?)', base_id, title, description, image_url, price
+ else
+ output.execute 'UPDATE products ' +
+ 'SET base_id=?, title=?, description=?, image_url=?, price=? WHERE id=?',
+ base_id, title, description, image_url, price, id.first
+ end
end
input.close
diff --git a/products.db b/products.db
index 1694c44..7f494fa 100644
Binary files a/products.db and b/products.db differ
Commit all of the changes.
git commit -a -m "update via raw SQLite3"
Created commit 95019bc: update via raw SQLite3
2 files changed, 41 insertions(+), 27 deletions(-)
rewrite load_products.rb (68%)
Our code is SQLite3 specific (for deployment, we might prefer MySQL or Oracle or DB2...), and is starting to get crufty. Let's see if ActiveRecord can simplify things.
establish_connection, Schema.define, find_by_base_id, save!
edit load_products.rb
require 'rexml/document'
require 'rubygems'
require 'active_record'
input = File.new('testdata.xml')
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'products.db')
class Product < ActiveRecord::Base
unless table_exists?
ActiveRecord::Schema.define do
create_table :products do |t|
t.integer :base_id
t.string :title
t.text :description
t.string :image_url
t.decimal :price, :precision=>8, :scale=>2, :default=>0.0
end
end
end
end
REXML::Document.new(input).each_element('//product') do |xproduct|
base_id = xproduct.elements['id'].text
product = Product.find_by_base_id(base_id) || Product.new
product.base_id = base_id
product.title = xproduct.elements['title'].text
product.description = xproduct.elements['description'].text
product.image_url = xproduct.elements['image-url'].text
product.price = xproduct.elements['price'].text
product.save!
end
input.close
ActiveRecord::Base.remove_connection
Run the same test.
ruby load_products.rb
ruby test_products.rb
Loaded suite test_products
Started
...
Finished in 0.018658 seconds.
3 tests, 12 assertions, 0 failures, 0 errors
Commit changes.
git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: load_products.rb
#
no changes added to commit (use "git add" and/or "git commit -a")
git commit -a -m "update using ActiveRecord"
Created commit 6f0ddd9: update using ActiveRecord
1 files changed, 40 insertions(+), 41 deletions(-)
rewrite load_products.rb (92%)
View the log of changes made so far.
git log
commit 6f0ddd919be9f5587ab0db06115334aba332ada1
Author: Sam Ruby <rubys@intertwingly.net>
Date: Sat Jul 11 12:38:37 2009 -0400
update using ActiveRecord
commit 95019bc45fb58154de46f82717c8a6a6ad3a1b96
Author: Sam Ruby <rubys@intertwingly.net>
Date: Sat Jul 11 12:38:36 2009 -0400
update via raw SQLite3
commit a679b97d9ccf5e0f34514dbf212b8f32bcd6cf3a
Author: Sam Ruby <rubys@intertwingly.net>
Date: Sat Jul 11 12:38:36 2009 -0400
load via raw SQLite3
Now, lets get that data to display in the brower, using the simplest thing that could possibly work, namely Rack.
Tests: response OK, 3 products, and verify one title.
edit test_product_server.rb
require 'product_server'
require 'test/unit'
require 'rack/test'
class ProductServerTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
ProductServer.new
end
def test_product_list
response = get('/products')
assert response.ok?
assert 3, response.body.scan('<h2>').count
assert_match /<h2>Pragmatic Project Automation<\/h2>/, response.body
end
end
Code: Establish connection, use Builder, and send response.
edit product_server.rb
require 'rubygems'
require 'rack'
require 'builder'
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'products.db')
class Product < ActiveRecord::Base
end
class ProductServer
def call(env)
x = Builder::XmlMarkup.new :indent=>2
x.html do
x.head do
x.title 'Pragmatic Bookshelf'
end
x.body do
x.h1 'Pragmatic Bookshelf'
Product.all.each do |product|
x.h2 product.title
x << " #{product.description}\n"
x.p product.price
end
end
end
response = Rack::Response.new
response['Content-Type'] = 'text/html'
response.write x.target!
response.finish
end
end
Test the server logic.
ruby test_product_server.rb
Loaded suite test_product_server
Started
.
Finished in 0.046949 seconds.
1 tests, 3 assertions, 0 failures, 0 errors
Minimal rack configuration.
edit config.ru
require 'product_server'
use Rack::ShowExceptions
map '/products' do
run ProductServer.new
end
See the output produced.
get /products
Pragmatic Project Automation shows you how to improve the consistency and repeatability of your project's procedures using automation to reduce risk and errors.
Simply put, we're going to put this thing called a computer to work for you doing the mundane (but important) project stuff. That means you'll have more time and energy to do the really exciting---and difficult---stuff, like writing quality code.
24.95
This book is a recipe-based approach to using Subversion that will get you up and running quickly---and correctly. All projects need version control: it's a foundational piece of any project's infrastructure. Yet half of all project teams in the U.S. don't use any version control at all. Many others don't use it well, and end up experiencing time-consuming problems.
28.5
Pragmatic programmers use feedback to drive their development and personal processes. The most valuable feedback you can get while coding comes from unit testing.
Without good tests in place, coding can become a frustrating game of "whack-a-mole." That's the carnival game where the player strikes at a mechanical mole; it retreats and another mole pops up on the opposite side of the field. The moles pop up and down so fast that you end up flailing your mallet helplessly as the moles continue to pop up where you least expect them.
27.75
See what file we changed.
git status
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# config.ru
# product_server.rb
# test_product_server.rb
nothing added to commit but untracked files present (use "git add" to track)
Add in the new files.
git add *server.rb config.ru
Commit the changes.
git commit -m "rack server"
Created commit aa60f15: rack server
3 files changed, 62 insertions(+), 0 deletions(-)
create mode 100644 config.ru
create mode 100644 product_server.rb
create mode 100644 test_product_server.rb
Make the test data viewable
mkdir public
git mv testdata.xml public
Update the rack configuration.
edit config.ru
require 'product_server'
use Rack::ShowExceptions
map '/products' do
run ProductServer.new
end
map '/' do
run Rack::File.new('public')
end
Get the test data.
get /testdata.xml
<?xml version="1.0" encoding="UTF-8"?>
<products type="array">
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
<em>Pragmatic Project Automation</em> shows you how to improve the
consistency and repeatability of your project's procedures using
automation to reduce risk and errors.
</p>
<p>
Simply put, we're going to put this thing called a computer to work
for you doing the mundane (but important) project stuff. That means
you'll have more time and energy to do the really
exciting---and difficult---stuff, like writing quality code.
</p></description>
<id type="integer">2</id>
<image-url>/images/auto.jpg</image-url>
<price type="decimal">24.95</price>
<title>Pragmatic Project Automation</title>
<updated-at type="datetime">2009-07-05T16:32:09Z</updated-at>
</product>
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
This book is a recipe-based approach to using Subversion that will
get you up and running quickly---and correctly. All projects need
version control: it's a foundational piece of any project's
infrastructure. Yet half of all project teams in the U.S. don't use
any version control at all. Many others don't use it well, and end
up experiencing time-consuming problems.
</p></description>
<id type="integer">3</id>
<image-url>/images/svn.jpg</image-url>
<price type="decimal">28.5</price>
<title>Pragmatic Version Control</title>
<updated-at type="datetime">2009-07-05T16:26:31Z</updated-at>
</product>
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
Pragmatic programmers use feedback to drive their development and
personal processes. The most valuable feedback you can get while
coding comes from unit testing.
</p>
<p>
Without good tests in place, coding can become a frustrating game of
"whack-a-mole." That's the carnival game where the player strikes at a
mechanical mole; it retreats and another mole pops up on the opposite side
of the field. The moles pop up and down so fast that you end up flailing
your mallet helplessly as the moles continue to pop up where you least
expect them.
</p></description>
<id type="integer">4</id>
<image-url>/images/utc.jpg</image-url>
<price type="decimal">27.75</price>
<title>Pragmatic Unit Testing (C#)</title>
<updated-at type="datetime">2009-07-05T16:26:31Z</updated-at>
</product>
</products>
Update the loader script with the new location.
edit load_products.rb
require 'rexml/document'
require 'rubygems'
require 'active_record'
input = File.new('public/testdata.xml')
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'products.db')
class Product < ActiveRecord::Base
unless table_exists?
ActiveRecord::Schema.define do
create_table :products do |t|
t.integer :base_id
t.string :title
t.text :description
t.string :image_url
t.decimal :price, :precision=>8, :scale=>2, :default=>0.0
end
end
end
end
REXML::Document.new(input).each_element('//product') do |xproduct|
base_id = xproduct.elements['id'].text
product = Product.find_by_base_id(base_id) || Product.new
product.base_id = base_id
product.title = xproduct.elements['title'].text
product.description = xproduct.elements['description'].text
product.image_url = xproduct.elements['image-url'].text
product.price = xproduct.elements['price'].text
product.save!
end
input.close
ActiveRecord::Base.remove_connection
Verify the changes.
rm products.db
ruby load_products.rb
-- create_table(:products)
-> 0.0278s
ruby test_products.rb
Loaded suite test_products
Started
...
Finished in 0.010468 seconds.
3 tests, 12 assertions, 0 failures, 0 errors
Commit the results.
git commit -a -m "serve testdata"
Created commit 8aba582: serve testdata
4 files changed, 5 insertions(+), 1 deletions(-)
rename testdata.xml => public/testdata.xml (100%)
We've got the program working on our machine, let's deploy it to our server machine which is running Passenger (a.k.a. mod_rails a.k.a. mod_rack) on Apache's http. This takes a bit of planning the first time, but then Capistrano takes all of the guesswork and potential for errors out of the equation when it really matters. Note: this step can be safely skipped on first reading.
Create our Capistrano configuration
capify .
[skip] `./Capfile' already exists
[add] writing `./Capfile'
[add] writing `./config/deploy.rb'
[done] capified!
Tailor it extensively
edit config/deploy.rb
# be sure to change these
set :user, 'sa3ruby'
set :domain, 'depot.intertwingly.net'
set :application, 'depot'
# file paths
set :repository, "#{user}@#{domain}:git/#{application}.git"
set :deploy_to, "/home/#{user}/#{domain}"
# distribute your applications across servers (the instructions below put them
# all on the same server, defined above as 'domain', adjust as necessary)
role :app, domain
role :web, domain
role :db, domain, :primary => true
# you might need to set this if you aren't seeing password prompts
default_run_options[:pty] = true
# As Capistrano executes in a non-interactive mode and therefore doesn't cause
# any of your shell profile scripts to be run, the following might be needed
# if (for example) you have locally installed gems or applications. Note:
# this needs to contain the full values for the variables set, not simply
# the deltas.
default_environment['PATH']='$HOME/.gems/bin:/usr/local/bin:/usr/bin:/bin'
default_environment['GEM_PATH']='$HOME/.gems:/usr/lib/ruby/gems/1.8'
# miscellaneous options
set :deploy_via, :remote_cache
set :scm, 'git'
set :branch, 'master'
set :scm_verbose, true
set :use_sudo, false
# additional tasks
namespace :deploy do
desc 'Restarting Passenger'
task :restart, :roles => :app do
run "mkdir -p #{current_path}/tmp"
run "touch #{current_path}/tmp/restart.txt"
end
end
# insert tasks into the deployment sequence
after "deploy:symlink", "deploy:restart"
Commit to the repository.
git status
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# Capfile
# config/
nothing added to commit but untracked files present (use "git add" to track)
git add config Capfile
git commit -m "capify"
Created commit e9cb0f5: capify
2 files changed, 47 insertions(+), 0 deletions(-)
create mode 100644 Capfile
create mode 100644 config/deploy.rb
Push the repository to the server.
git remote add origin ssh://sa3ruby@depot.intertwingly.net/~/git/depot.git
git push origin master
Counting objects: 29, done.
Compressing objects: 3% (1/27) Compressing objects: 7% (2/27) Compressing objects: 11% (3/27) Compressing objects: 14% (4/27) Compressing objects: 18% (5/27) Compressing objects: 22% (6/27) Compressing objects: 25% (7/27) Compressing objects: 29% (8/27) Compressing objects: 33% (9/27) Compressing objects: 37% (10/27) Compressing objects: 40% (11/27) Compressing objects: 44% (12/27) Compressing objects: 48% (13/27) Compressing objects: 51% (14/27) Compressing objects: 55% (15/27) Compressing objects: 59% (16/27) Compressing objects: 62% (17/27) Compressing objects: 66% (18/27) Compressing objects: 70% (19/27) Compressing objects: 74% (20/27) Compressing objects: 77% (21/27) Compressing objects: 81% (22/27) Compressing objects: 85% (23/27) Compressing objects: 88% (24/27) Compressing objects: 92% (25/27) Compressing objects: 96% (26/27) Compressing objects: 100% (27/27) Compressing objects: 100% (27/27), done.
Writing objects: 3% (1/29) Writing objects: 6% (2/29) Writing objects: 10% (3/29) Writing objects: 13% (4/29) Writing objects: 17% (5/29) Writing objects: 20% (6/29) Writing objects: 24% (7/29) Writing objects: 27% (8/29) Writing objects: 31% (9/29) Writing objects: 34% (10/29) Writing objects: 37% (11/29) Writing objects: 41% (12/29) Writing objects: 44% (13/29) Writing objects: 48% (14/29) Writing objects: 51% (15/29) Writing objects: 55% (16/29) Writing objects: 58% (17/29) Writing objects: 62% (18/29) Writing objects: 65% (19/29) Writing objects: 68% (20/29) Writing objects: 72% (21/29) Writing objects: 75% (22/29) Writing objects: 79% (23/29) Writing objects: 82% (24/29) Writing objects: 86% (25/29) Writing objects: 89% (26/29) Writing objects: 93% (27/29) Writing objects: 96% (28/29) Writing objects: 100% (29/29) Writing objects: 100% (29/29), 7.77 KiB, done.
Total 29 (delta 8), reused 0 (delta 0)
To ssh://sa3ruby@depot.intertwingly.net/~/git/depot.git
* [new branch] master -> master
Allow Capistrano to set up the server.
cap deploy:setup
* executing `deploy:setup'
* executing "mkdir -p /home/sa3ruby/depot.intertwingly.net /home/sa3ruby/depot.intertwingly.net/releases /home/sa3ruby/depot.intertwingly.net/shared /home/sa3ruby/depot.intertwingly.net/shared/system /home/sa3ruby/depot.intertwingly.net/shared/log /home/sa3ruby/depot.intertwingly.net/shared/pids && chmod g+w /home/sa3ruby/depot.intertwingly.net /home/sa3ruby/depot.intertwingly.net/releases /home/sa3ruby/depot.intertwingly.net/shared /home/sa3ruby/depot.intertwingly.net/shared/system /home/sa3ruby/depot.intertwingly.net/shared/log /home/sa3ruby/depot.intertwingly.net/shared/pids"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
Check that the server is ready for deployment.
cap deploy:check
* executing `deploy:check'
* executing "test -d /home/sa3ruby/depot.intertwingly.net/releases"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "test -w /home/sa3ruby/depot.intertwingly.net"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "test -w /home/sa3ruby/depot.intertwingly.net/releases"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "which git"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "test -w /home/sa3ruby/depot.intertwingly.net/shared"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
You appear to have all necessary dependencies installed
Do the deployment.
cap deploy
* executing `deploy'
* executing `deploy:update'
** transaction: start
* executing `deploy:update_code'
updating the cached checkout on all servers
executing locally: "git ls-remote sa3ruby@depot.intertwingly.net:git/depot.git master"
* executing "if [ -d /home/sa3ruby/depot.intertwingly.net/shared/cached-copy ]; then cd /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && git fetch origin && git reset --hard e9cb0f5100b5fcb7ceda7514044d0eab49117648 && git clean -d -x -f; else git clone sa3ruby@depot.intertwingly.net:git/depot.git /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && cd /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && git checkout -b deploy e9cb0f5100b5fcb7ceda7514044d0eab49117648; fi"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
** [depot.intertwingly.net :: out] Initialized empty Git repository in /home/sa3ruby/depot.intertwingly.net/shared/cached-copy/.git/
** [depot.intertwingly.net :: out] remote: Counting objects: 29, done.*[K
** remote: Compressing objects: 3% (1/27) *[K remote: Compressing objects: 7% (2/27) *[K remote: Compressing objects: 11% (*[K3remote: /27) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 14% (4/27) *[K remote: Compressing objects: 18% (5/27) *[K remote: Compressing objects: 22% (6/27) *[K remote: Compressing*[K remote: objects: 25% (7/27) *[K remote: Compressing objects: 29% (8/27) *[K remote: Compressing objects: 33% (9/27) *[K remote: Compressing objects: 37% (10/2*[K7remote: ) *[K remote: Compressing objects: 40% (11/27) *[K remote: Compressing objects: 44% (12/27) *[K remote: Compressing objects: 48% (13/27) *[K remote: Compressing*[K remote: objects: 51% (14/27) *[K remote: Compressing objects: 55% (15/27) *[K remote: Compressing objects: 59% (16/27) *[K remote: Compressing objects: 62% (1*[K7remote: /27) *[K remote: Compressing objects: 66% (18/27) *[K remote: Compressing objects: 70% (19/27) *[K remote: Compressing objects: 74% (20/27) *[K remote: Compress*[Ki
** [depot.intertwingly.net :: out] remote: ng objects: 77% (21/27) *[K remote: Compressing objects: 81% (22/27) *[K remote: Compressing objects: 85% (23/27) *[K remote: Compressing objects: 88%*[K remote: (24/27) *[K remote: Compressing objects: 92% (25/27) *[K remote: Compressing objects: 96% (26/27) *[K remote: Compressing objects: 100% (27/27) *[K remote: Compr*[Keremote: ssing objects: 100% (27/27), done.*[K
** remote: Total 29 (delta 8), reused 0 (delta 0)*[K
** [depot.intertwingly.net :: out] Receiving objects: 3% (1/29)
** [depot.intertwingly.net :: out] Receiving objects: 6% (2/29)
** [depot.intertwingly.net :: out] Receiving objects: 10% (3/29)
** [depot.intertwingly.net :: out] Receiving objects: 13% (4/29)
** [depot.intertwingly.net :: out] Receiving objects: 17% (5/29)
** [depot.intertwingly.net :: out] Receiving objects: 20% (6/29)
** [depot.intertwingly.net :: out] Receiving objects: 24% (7/29)
** [depot.intertwingly.net :: out] Receiving objects: 27% (8/29)
** [depot.intertwingly.net :: out] Receiving objects: 31% (9/29)
** [depot.intertwingly.net :: out] Receiving objects: 34% (10/29)
** [depot.intertwingly.net :: out] Receiving objects: 37% (11/29)
** [depot.intertwingly.net :: out] Receiving objects: 41% (12/29)
** [depot.intertwingly.net :: out] Receiving objects: 44% (13/29)
** [depot.intertwingly.net :: out] Receiving objects: 48% (14/29)
** [depot.intertwingly.net :: out] Receiving objects: 51% (15/29)
** [depot.intertwingly.net :: out] Receiving objects: 55% (16/29)
** [depot.intertwingly.net :: out] Receiving objects: 58% (17/29)
** [depot.intertwingly.net :: out] Receiving objects: 62% (18/29)
** [depot.intertwingly.net :: out] Receiving objects: 65% (19/29)
** [depot.intertwingly.net :: out] Receiving objects: 68% (20/29)
** [depot.intertwingly.net :: out] Receiving objects: 72% (21/29) Receiving objects: 75% (22/29) Receiving objects: 79% (23/29)
** [depot.intertwingly.net :: out] Receiving objects: 82% (24/29) Receiving objects: 86% (25/29)
** [depot.intertwingly.net :: out] Receiving objects: 89% (26/29)
** [depot.intertwingly.net :: out] Receiving objects: 93% (27/29)
** [depot.intertwingly.net :: out] Receiving objects: 96% (28/29) Receiving objects: 100% (29/29)
** [depot.intertwingly.net :: out] Receiving objects: 100% (29/29), 7.63 KiB, done.
** [depot.intertwingly.net :: out] Resolving deltas: 12% (1/8)
** [depot.intertwingly.net :: out] Resolving deltas: 25% (2/8)
** [depot.intertwingly.net :: out] Resolving deltas: 37% (3/8)
** [depot.intertwingly.net :: out] Resolving deltas: 62% (5/8)
** [depot.intertwingly.net :: out] Resolving deltas: 87% (7/8)
** [depot.intertwingly.net :: out] Resolving deltas: 100% (8/8)
** [depot.intertwingly.net :: out] Resolving deltas: 100% (8/8), done.
** [depot.intertwingly.net :: out] Switched to a new branch "deploy"
command finished
copying the cached version to /home/sa3ruby/depot.intertwingly.net/releases/20090711163908
* executing "cp -RPp /home/sa3ruby/depot.intertwingly.net/shared/cached-copy /home/sa3ruby/depot.intertwingly.net/releases/20090711163908 && (echo e9cb0f5100b5fcb7ceda7514044d0eab49117648 > /home/sa3ruby/depot.intertwingly.net/releases/20090711163908/REVISION)"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing `deploy:finalize_update'
* executing "chmod -R g+w /home/sa3ruby/depot.intertwingly.net/releases/20090711163908"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing `deploy:symlink'
* executing "rm -f /home/sa3ruby/depot.intertwingly.net/current && ln -s /home/sa3ruby/depot.intertwingly.net/releases/20090711163908 /home/sa3ruby/depot.intertwingly.net/current"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
triggering after callbacks for `deploy:symlink'
* executing `deploy:restart'
* executing "mkdir -p /home/sa3ruby/depot.intertwingly.net/current/tmp"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "touch /home/sa3ruby/depot.intertwingly.net/current/tmp/restart.txt"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
** transaction: commit
See the results.
get http://depot.intertwingly.net/products
Pragmatic Project Automation shows you how to improve the consistency and repeatability of your project's procedures using automation to reduce risk and errors.
Simply put, we're going to put this thing called a computer to work for you doing the mundane (but important) project stuff. That means you'll have more time and energy to do the really exciting---and difficult---stuff, like writing quality code.
24.95
This book is a recipe-based approach to using Subversion that will get you up and running quickly---and correctly. All projects need version control: it's a foundational piece of any project's infrastructure. Yet half of all project teams in the U.S. don't use any version control at all. Many others don't use it well, and end up experiencing time-consuming problems.
28.5
Pragmatic programmers use feedback to drive their development and personal processes. The most valuable feedback you can get while coding comes from unit testing.
Without good tests in place, coding can become a frustrating game of "whack-a-mole." That's the carnival game where the player strikes at a mechanical mole; it retreats and another mole pops up on the opposite side of the field. The moles pop up and down so fast that you end up flailing your mallet helplessly as the moles continue to pop up where you least expect them.
27.75
get http://depot.intertwingly.net/testdata.xml
<?xml version="1.0" encoding="UTF-8"?>
<products type="array">
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
<em>Pragmatic Project Automation</em> shows you how to improve the
consistency and repeatability of your project's procedures using
automation to reduce risk and errors.
</p>
<p>
Simply put, we're going to put this thing called a computer to work
for you doing the mundane (but important) project stuff. That means
you'll have more time and energy to do the really
exciting---and difficult---stuff, like writing quality code.
</p></description>
<id type="integer">2</id>
<image-url>/images/auto.jpg</image-url>
<price type="decimal">24.95</price>
<title>Pragmatic Project Automation</title>
<updated-at type="datetime">2009-07-05T16:32:09Z</updated-at>
</product>
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
This book is a recipe-based approach to using Subversion that will
get you up and running quickly---and correctly. All projects need
version control: it's a foundational piece of any project's
infrastructure. Yet half of all project teams in the U.S. don't use
any version control at all. Many others don't use it well, and end
up experiencing time-consuming problems.
</p></description>
<id type="integer">3</id>
<image-url>/images/svn.jpg</image-url>
<price type="decimal">28.5</price>
<title>Pragmatic Version Control</title>
<updated-at type="datetime">2009-07-05T16:26:31Z</updated-at>
</product>
<product>
<created-at type="datetime">2009-07-05T16:26:31Z</created-at>
<description><p>
Pragmatic programmers use feedback to drive their development and
personal processes. The most valuable feedback you can get while
coding comes from unit testing.
</p>
<p>
Without good tests in place, coding can become a frustrating game of
"whack-a-mole." That's the carnival game where the player strikes at a
mechanical mole; it retreats and another mole pops up on the opposite side
of the field. The moles pop up and down so fast that you end up flailing
your mallet helplessly as the moles continue to pop up where you least
expect them.
</p></description>
<id type="integer">4</id>
<image-url>/images/utc.jpg</image-url>
<price type="decimal">27.75</price>
<title>Pragmatic Unit Testing (C#)</title>
<updated-at type="datetime">2009-07-05T16:26:31Z</updated-at>
</product>
</products>
At this point, we are displaying a what amounts to be static data. Presumably the supplier will be making changes, so let's set things up so that everything is updated every morning, before we wake up.
Load from the web (yes, this is our server, work with me for now)
edit load_products.rb
require 'net/http'
require 'rexml/document'
require 'rubygems'
require 'active_record'
input = Net::HTTP.get(URI.parse(ARGV.first))
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'products.db')
class Product < ActiveRecord::Base
unless table_exists?
ActiveRecord::Schema.define do
create_table :products do |t|
t.integer :base_id
t.string :title
t.text :description
t.string :image_url
t.decimal :price, :precision=>8, :scale=>2, :default=>0.0
end
end
end
end
REXML::Document.new(input).each_element('//product') do |xproduct|
base_id = xproduct.elements['id'].text
product = Product.find_by_base_id(base_id) || Product.new
product.base_id = base_id
product.title = xproduct.elements['title'].text
product.description = xproduct.elements['description'].text
product.image_url = xproduct.elements['image-url'].text
product.price = xproduct.elements['price'].text
product.save!
end
ActiveRecord::Base.remove_connection
Verify that the change works
rm products.db
ruby load_products.rb http://depot.intertwingly.net/testdata.xml
-- create_table(:products)
-> 0.0155s
ruby test_products.rb
Loaded suite test_products
Started
...
Finished in 0.04859 seconds.
3 tests, 12 assertions, 0 failures, 0 errors
Set up whenever
wheneverize .
[add] writing `./config/schedule.rb'
[done] wheneverized!
Add a command to run load_products daily at 4:15 am
edit config/schedule.rb
# Use this file to easily define all of your cron jobs.
#
# It's helpful, but not entirely necessary to understand cron before proceeding.
# http://en.wikipedia.org/wiki/Cron
# Example:
#
# set :cron_log, "/path/to/my/cron_log.log"
#
# every 2.hours do
# command "/usr/bin/some_great_command"
# runner "MyModel.some_method"
# rake "some:great:rake:task"
# end
#
# every 4.days do
# runner "AnotherModel.prune_old_records"
# end
# Learn more: http://github.com/javan/whenever
root = File.dirname(File.expand_path(__FILE__))
every 1.day, :at => '4:15 am' do
command "cd #{root}; ruby load_products.rb http://depot.intertwingly.net/testdata.xml"
end
Visually inspect what the crontab entry looks like.
whenever
15 4 * * * cd /home/rubys/git/awdwr/work/depot; ruby load_products.rb http://depot.intertwingly.net/testdata.xml
edit config/deploy.rb
# be sure to change these
set :user, 'sa3ruby'
set :domain, 'depot.intertwingly.net'
set :application, 'depot'
# file paths
set :repository, "#{user}@#{domain}:git/#{application}.git"
set :deploy_to, "/home/#{user}/#{domain}"
# distribute your applications across servers (the instructions below put them
# all on the same server, defined above as 'domain', adjust as necessary)
role :app, domain
role :web, domain
role :db, domain, :primary => true
# you might need to set this if you aren't seeing password prompts
default_run_options[:pty] = true
# As Capistrano executes in a non-interactive mode and therefore doesn't cause
# any of your shell profile scripts to be run, the following might be needed
# if (for example) you have locally installed gems or applications. Note:
# this needs to contain the full values for the variables set, not simply
# the deltas.
default_environment['PATH']='$HOME/.gems/bin:/usr/local/bin:/usr/bin:/bin'
default_environment['GEM_PATH']='$HOME/.gems:/usr/lib/ruby/gems/1.8'
# miscellaneous options
set :deploy_via, :remote_cache
set :scm, 'git'
set :branch, 'master'
set :scm_verbose, true
set :use_sudo, false
# additional tasks
namespace :deploy do
desc 'Restarting Passenger'
task :restart, :roles => :app do
run "mkdir -p #{current_path}/tmp"
run "touch #{current_path}/tmp/restart.txt"
end
desc 'Update the crontab file'
task :update_crontab, :roles => :db do
run "cd #{current_path} && whenever --update-crontab #{application}"
end
end
# insert tasks into the deployment sequence
after "deploy:symlink", "deploy:restart"
after "deploy:restart", "deploy:update_crontab"
Commit the changes.
git st
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: config/deploy.rb
# modified: load_products.rb
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# config/schedule.rb
no changes added to commit (use "git add" and/or "git commit -a")
git add config/schedule.rb
git commit -a -m "whenever"
Created commit 4eaa140: whenever
3 files changed, 34 insertions(+), 2 deletions(-)
create mode 100644 config/schedule.rb
Push and deploy!
git push
Counting objects: 10, done.
Compressing objects: 16% (1/6) Compressing objects: 33% (2/6) Compressing objects: 50% (3/6) Compressing objects: 66% (4/6) Compressing objects: 83% (5/6) Compressing objects: 100% (6/6) Compressing objects: 100% (6/6), done.
Writing objects: 16% (1/6) Writing objects: 33% (2/6) Writing objects: 50% (3/6) Writing objects: 66% (4/6) Writing objects: 83% (5/6) Writing objects: 100% (6/6) Writing objects: 100% (6/6), 1.06 KiB, done.
Total 6 (delta 3), reused 0 (delta 0)
To ssh://sa3ruby@depot.intertwingly.net/~/git/depot.git
e9cb0f5..4eaa140 master -> master
cap deploy
* executing `deploy'
* executing `deploy:update'
** transaction: start
* executing `deploy:update_code'
updating the cached checkout on all servers
executing locally: "git ls-remote sa3ruby@depot.intertwingly.net:git/depot.git master"
* executing "if [ -d /home/sa3ruby/depot.intertwingly.net/shared/cached-copy ]; then cd /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && git fetch origin && git reset --hard 4eaa140c4d4587119f0d10ec0705a72db8c02793 && git clean -d -x -f; else git clone sa3ruby@depot.intertwingly.net:git/depot.git /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && cd /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && git checkout -b deploy 4eaa140c4d4587119f0d10ec0705a72db8c02793; fi"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
** [depot.intertwingly.net :: out] remote: Counting objects: 10, done.*[K
** remote: Compressing objects: 16% (1/6) *[K remote: Compressing objects: 33% (2/6) *[K remote: Compressing objects: 50% (3/6) *[K remote: Compressing objects: *[K6
** [depot.intertwingly.net :: out] remote: 6% (4/6) *[K remote: Compressing objects: 83% (5/6) *[K remote: Compressing objects: 100% (6/6) *[K remote: Compressing objects: 100% (6/6), done.*[K
** remote: Total *[K6remote: (delta 3), reused 0 (delta 0)*[K
** [depot.intertwingly.net :: out] Unpacking objects: 16% (1/6)
** [depot.intertwingly.net :: out] Unpacking objects: 33% (2/6)
** [depot.intertwingly.net :: out] Unpacking objects: 50% (3/6)
** [depot.intertwingly.net :: out] Unpacking objects: 66% (4/6)
** [depot.intertwingly.net :: out] Unpacking objects: 83% (5/6)
** [depot.intertwingly.net :: out] Unpacking objects: 100% (6/6) Unpacking objects: 100% (6/6), done.
** [depot.intertwingly.net :: out] From sa3ruby@depot.intertwingly.net:git/depot
** e9cb0f5..4eaa140 master -> origin/master
** [depot.intertwingly.net :: out] HEAD is now at 4eaa140 whenever
command finished
copying the cached version to /home/sa3ruby/depot.intertwingly.net/releases/20090711163919
* executing "cp -RPp /home/sa3ruby/depot.intertwingly.net/shared/cached-copy /home/sa3ruby/depot.intertwingly.net/releases/20090711163919 && (echo 4eaa140c4d4587119f0d10ec0705a72db8c02793 > /home/sa3ruby/depot.intertwingly.net/releases/20090711163919/REVISION)"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing `deploy:finalize_update'
* executing "chmod -R g+w /home/sa3ruby/depot.intertwingly.net/releases/20090711163919"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing `deploy:symlink'
* executing "rm -f /home/sa3ruby/depot.intertwingly.net/current && ln -s /home/sa3ruby/depot.intertwingly.net/releases/20090711163919 /home/sa3ruby/depot.intertwingly.net/current"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
triggering after callbacks for `deploy:symlink'
* executing `deploy:restart'
* executing "mkdir -p /home/sa3ruby/depot.intertwingly.net/current/tmp"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "touch /home/sa3ruby/depot.intertwingly.net/current/tmp/restart.txt"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
triggering after callbacks for `deploy:restart'
* executing `deploy:update_crontab'
* executing "cd /home/sa3ruby/depot.intertwingly.net/current && whenever --update-crontab depot"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
** [out :: depot.intertwingly.net] [write] crontab file updated
command finished
** transaction: commit
Taking a step back, we have done something real. It doesn't do much, but it didn't really take much code either. But the problems are starting to accumulate: our application directory is getting cluttered, changes to schemas is a problem, we have code duplicated that establishes the connection, and we haven't even begun thinking about updates. Additionally, we have the database in the git repository and while that has proven to be convenient so far, that won't be such a hot idea once we deploy. And synchronizing gems versions between the machines is a pain... Fred Brooks once recommended that we "plan to throw one away; you will, anyhow." As you will see, we are not exactly going to be throwing anything away, but we will be in a very real sense starting over.
First, let Rails do its thing...
cd ..; rails depot
exists
create app/controllers
create app/helpers
create app/models
create app/views/layouts
create config/environments
create config/initializers
create config/locales
create db
create doc
create lib
create lib/tasks
create log
create public/images
create public/javascripts
create public/stylesheets
create script/performance
create test/fixtures
create test/functional
create test/integration
create test/performance
create test/unit
create vendor
create vendor/plugins
create tmp/sessions
create tmp/sockets
create tmp/cache
create tmp/pids
create Rakefile
create README
create app/controllers/application_controller.rb
create app/helpers/application_helper.rb
create config/database.yml
create config/routes.rb
create config/locales/en.yml
create config/initializers/backtrace_silencers.rb
create config/initializers/inflections.rb
create config/initializers/mime_types.rb
create config/initializers/new_rails_defaults.rb
create config/initializers/session_store.rb
create config/environment.rb
create config/boot.rb
create config/environments/production.rb
create config/environments/development.rb
create config/environments/test.rb
create script/about
create script/console
create script/dbconsole
create script/destroy
create script/generate
create script/runner
create script/server
create script/plugin
create script/performance/benchmarker
create script/performance/profiler
create test/test_helper.rb
create test/performance/browsing_test.rb
create public/404.html
create public/422.html
create public/500.html
create public/index.html
create public/favicon.ico
create public/robots.txt
create public/images/rails.png
create public/javascripts/prototype.js
create public/javascripts/effects.js
create public/javascripts/dragdrop.js
create public/javascripts/controls.js
create public/javascripts/application.js
create doc/README_FOR_APP
create log/server.log
create log/production.log
create log/development.log
create log/test.log
Throw away the Rack bootstrap, it served us well.
git rm config.ru
rm 'config.ru'
Define the product anew.
ruby script/generate scaffold product base_id:integer title:string description:text image_url:string price:decimal
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/products
exists app/views/layouts/
exists test/functional/
exists test/unit/
create test/unit/helpers/
exists public/stylesheets/
create app/views/products/index.html.erb
create app/views/products/show.html.erb
create app/views/products/new.html.erb
create app/views/products/edit.html.erb
create app/views/layouts/products.html.erb
create public/stylesheets/scaffold.css
create app/controllers/products_controller.rb
create test/functional/products_controller_test.rb
create app/helpers/products_helper.rb
create test/unit/helpers/products_helper_test.rb
route map.resources :products
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/product.rb
create test/unit/product_test.rb
create test/fixtures/products.yml
create db/migrate
create db/migrate/20090711163925_create_products.rb
Tailor the definition to taste
edit db/migrate/20090711163925_create_products.rb
class CreateProducts < ActiveRecord::Migration
def self.up
create_table :products do |t|
t.integer :base_id
t.string :title
t.text :description
t.string :image_url
t.decimal :price, :precision => 8, :scale => 2, :default => 0
t.timestamps
end
end
def self.down
drop_table :products
end
end
Out with the old db.
git rm products.db
rm 'products.db'
In with the new.
rake db:migrate
mv 20090711163925_create_products.rb 20080601000001_create_products.rb
(in /home/rubys/git/awdwr/work/depot)
== CreateProducts: migrating =================================================
-- create_table(:products)
-> 0.0112s
== CreateProducts: migrated (0.0113s) ========================================
Write unit tests (this time using ActiveRecord!)
edit test/unit/product_test.rb
require 'test_helper'
class ProductTest < ActiveSupport::TestCase
def setup
Product.import('public/testdata.xml')
end
test "Pragmatic Project Automation" do
product = Product.find_by_base_id(2)
assert_equal 'Pragmatic Project Automation', product.title
assert_match /^<p>\s+<em>Pragmatic Project Automation/, product.description
assert_equal '/images/auto.jpg', product.image_url
assert_equal 24.95, product.price
end
test "Pragmatic Version Control" do
product = Product.find_by_base_id(3)
assert_equal 'Pragmatic Version Control', product.title
assert_match /^<p>\s+This book is a recipe-based approach/, product.description
assert_equal '/images/svn.jpg', product.image_url
assert_equal 28.5, product.price
end
test "Pragmatic Unit Testing" do
product = Product.find_by_base_id(4)
assert_equal 'Pragmatic Unit Testing (C#)', product.title
assert_match /<p>\s+Pragmatic programmers use feedback/, product.description
assert_equal '/images/utc.jpg', product.image_url
assert_equal 27.75, product.price
end
end
Run the tests and watch them fail.
rake test:units
/usr/bin/ruby1.8 -I"lib:test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb" "test/unit/helpers/products_helper_test.rb" "test/unit/product_test.rb"
(in /home/rubys/git/awdwr/work/depot)
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
EEE
Finished in 0.090224 seconds.
1) Error:
test_Pragmatic_Project_Automation(ProductTest):
NoMethodError: undefined method `import' for #<Class:0x7fbaf3e43030>
/test/unit/product_test.rb:5:in `setup'
2) Error:
test_Pragmatic_Unit_Testing(ProductTest):
NoMethodError: undefined method `import' for #<Class:0x7fbaf3e43030>
/test/unit/product_test.rb:5:in `setup'
3) Error:
test_Pragmatic_Version_Control(ProductTest):
NoMethodError: undefined method `import' for #<Class:0x7fbaf3e43030>
/test/unit/product_test.rb:5:in `setup'
3 tests, 0 assertions, 0 failures, 3 errors
rake aborted!
Command failed with status (1): [/usr/bin/ruby1.8 -I"lib:test" "/usr/lib/ru...]
(See full trace by running task with --trace)
Put the load logic in the model (url or file: getting fancy!)
edit app/models/product.rb
class Product < ActiveRecord::Base
def self.import(source)
if source =~ /^http:/
input = Net::HTTP.get(URI.parse(source))
else
input = File.open(source) {|file| file.read}
end
REXML::Document.new(input).each_element('//product') do |xproduct|
base_id = xproduct.elements['id'].text
product = self.find_by_base_id(base_id) || self.new
product.base_id = base_id
product.title = xproduct.elements['title'].text
product.description = xproduct.elements['description'].text
product.image_url = xproduct.elements['image-url'].text
product.price = xproduct.elements['price'].text
product.save!
end
end
end
Run the tests and watch them pass.
rake test:units
/usr/bin/ruby1.8 -I"lib:test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb" "test/unit/helpers/products_helper_test.rb" "test/unit/product_test.rb"
(in /home/rubys/git/awdwr/work/depot)
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
...
Finished in 0.197805 seconds.
3 tests, 12 assertions, 0 failures, 0 errors
Function tests are already provided and they pass!
rake test:functionals
/usr/bin/ruby1.8 -I"lib:test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb" "test/functional/products_controller_test.rb"
(in /home/rubys/git/awdwr/work/depot)
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
.......
Finished in 0.147647 seconds.
7 tests, 10 assertions, 0 failures, 0 errors
remove old tests and server
git rm test_*.rb product_server.rb
rm 'product_server.rb'
rm 'test_product_server.rb'
rm 'test_products.rb'
Load testdata.
ruby script/runner 'Product.import("public/testdata.xml")'
Explore.
get /products
Base | Title | Description | Image url | Price | |||
---|---|---|---|---|---|---|---|
2 | Pragmatic Project Automation | <p> <em>Pragmatic Project Automation</em> shows you how to improve the consistency and repeatability of your project's procedures using automation to reduce risk and errors. </p> <p> Simply put, we're going to put this thing called a computer to work for you doing the mundane (but important) project stuff. That means you'll have more time and energy to do the really exciting---and difficult---stuff, like writing quality code. </p> | /images/auto.jpg | 24.95 | Show | Edit | Destroy |
3 | Pragmatic Version Control | <p> This book is a recipe-based approach to using Subversion that will get you up and running quickly---and correctly. All projects need version control: it's a foundational piece of any project's infrastructure. Yet half of all project teams in the U.S. don't use any version control at all. Many others don't use it well, and end up experiencing time-consuming problems. </p> | /images/svn.jpg | 28.5 | Show | Edit | Destroy |
4 | Pragmatic Unit Testing (C#) | <p> Pragmatic programmers use feedback to drive their development and personal processes. The most valuable feedback you can get while coding comes from unit testing. </p> <p> Without good tests in place, coding can become a frustrating game of "whack-a-mole." That's the carnival game where the player strikes at a mechanical mole; it retreats and another mole pops up on the opposite side of the field. The moles pop up and down so fast that you end up flailing your mallet helplessly as the moles continue to pop up where you least expect them. </p> | /images/utc.jpg | 27.75 | Show | Edit | Destroy |
get /products/1
Base: 2
Title: Pragmatic Project Automation
Description: <p> <em>Pragmatic Project Automation</em> shows you how to improve the consistency and repeatability of your project's procedures using automation to reduce risk and errors. </p> <p> Simply put, we're going to put this thing called a computer to work for you doing the mundane (but important) project stuff. That means you'll have more time and energy to do the really exciting---and difficult---stuff, like writing quality code. </p>
Image url: /images/auto.jpg
Price: 24.95
Edit | Backget /products/1/edit
get /products/new
Capistrano is understands Rails, but there are a few things you need to be aware of.
Update whenever to use runner.
edit config/schedule.rb
# Use this file to easily define all of your cron jobs.
#
# It's helpful, but not entirely necessary to understand cron before proceeding.
# http://en.wikipedia.org/wiki/Cron
# Example:
#
# set :cron_log, "/path/to/my/cron_log.log"
#
# every 2.hours do
# command "/usr/bin/some_great_command"
# runner "MyModel.some_method"
# rake "some:great:rake:task"
# end
#
# every 4.days do
# runner "AnotherModel.prune_old_records"
# end
# Learn more: http://github.com/javan/whenever
every 1.day, :at => '4:15 am' do
runner "Product.import('http://depot.intertwingly.net/testdata.xml')"
end
Peek at the results.
whenever
15 4 * * * /home/rubys/git/awdwr/work/depot/script/runner -e production "Product.import('http://depot.intertwingly.net/testdata.xml')"
Add GEM_HOME to environment.rb, migration tasks, and do cleanup.
edit config/deploy.rb
# be sure to change these
set :user, 'sa3ruby'
set :domain, 'depot.intertwingly.net'
set :application, 'depot'
# file paths
set :repository, "#{user}@#{domain}:git/#{application}.git"
set :deploy_to, "/home/#{user}/#{domain}"
set :gemhome, "/home/#{user}/.gems"
# distribute your applications across servers (the instructions below put them
# all on the same server, defined above as 'domain', adjust as necessary)
role :app, domain
role :web, domain
role :db, domain, :primary => true
# you might need to set this if you aren't seeing password prompts
default_run_options[:pty] = true
# As Capistrano executes in a non-interactive mode and therefore doesn't cause
# any of your shell profile scripts to be run, the following might be needed
# if (for example) you have locally installed gems or applications. Note:
# this needs to contain the full values for the variables set, not simply
# the deltas.
default_environment['PATH']='$HOME/.gems/bin:/usr/local/bin:/usr/bin:/bin'
default_environment['GEM_PATH']='$HOME/.gems:/usr/lib/ruby/gems/1.8'
# miscellaneous options
set :deploy_via, :remote_cache
set :scm, 'git'
set :branch, 'master'
set :scm_verbose, true
set :use_sudo, false
# additional tasks
namespace :deploy do
desc 'Restarting Passenger'
task :restart, :roles => :app do
run "mkdir -p #{current_path}/tmp"
run "touch #{current_path}/tmp/restart.txt"
end
desc 'Update the crontab file'
task :update_crontab, :roles => :db do
run "cd #{current_path} && whenever --update-crontab #{application}"
end
desc 'set db path outside of application directory'
task :set_db_path do
run "sed -i 's|db/production|#{deploy_to}/depot|' " +
"#{release_path}/config/database.yml"
end
desc 'set GEM_HOME in the environment'
task :set_gem_home do
run "sed -i '1iENV[%(GEM_HOME)]=%(#{gemhome})\\n' " +
"#{release_path}/config/environment.rb"
end
end
# insert tasks into the deployment sequence
after "deploy:update_code", "deploy:set_db_path"
after "deploy:update_code", "deploy:set_gem_home"
after "deploy:symlink", "deploy:restart"
after "deploy:restart", "deploy:cleanup"
after "deploy:restart", "deploy:update_crontab"
# Rails migration tasks
load 'ext/rails-database-migrations.rb'
Tell git what file NOT to retain
edit .gitignore
db/*.sqlite3
log/*.log
tmp/**/*
Commit. Push. Deploy.
git st
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: config.ru
# deleted: product_server.rb
# deleted: products.db
# deleted: test_product_server.rb
# deleted: test_products.rb
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: config/deploy.rb
# modified: config/schedule.rb
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# .gitignore
# README
# Rakefile
# app/
# config/boot.rb
# config/database.yml
# config/environment.rb
# config/environments/
# config/initializers/
# config/locales/
# config/routes.rb
# db/
# doc/
# public/404.html
# public/422.html
# public/500.html
# public/favicon.ico
# public/images/
# public/index.html
# public/javascripts/
# public/robots.txt
# public/stylesheets/
# script/
# test/
git add .
git commit -m "convert to Rails!"
Created commit 8dd1afc: convert to Rails!
64 files changed, 8915 insertions(+), 97 deletions(-)
create mode 100644 .gitignore
create mode 100644 README
create mode 100644 Rakefile
create mode 100644 app/controllers/application_controller.rb
create mode 100644 app/controllers/products_controller.rb
create mode 100644 app/helpers/application_helper.rb
create mode 100644 app/helpers/products_helper.rb
create mode 100644 app/models/product.rb
create mode 100644 app/views/layouts/products.html.erb
create mode 100644 app/views/products/edit.html.erb
create mode 100644 app/views/products/index.html.erb
create mode 100644 app/views/products/new.html.erb
create mode 100644 app/views/products/show.html.erb
delete mode 100644 config.ru
create mode 100644 config/boot.rb
create mode 100644 config/database.yml
create mode 100644 config/environment.rb
create mode 100644 config/environments/development.rb
create mode 100644 config/environments/production.rb
create mode 100644 config/environments/test.rb
create mode 100644 config/initializers/backtrace_silencers.rb
create mode 100644 config/initializers/inflections.rb
create mode 100644 config/initializers/mime_types.rb
create mode 100644 config/initializers/new_rails_defaults.rb
create mode 100644 config/initializers/session_store.rb
create mode 100644 config/locales/en.yml
create mode 100644 config/routes.rb
create mode 100644 db/migrate/20080601000001_create_products.rb
create mode 100644 db/schema.rb
create mode 100644 doc/README_FOR_APP
delete mode 100644 product_server.rb
delete mode 100644 products.db
create mode 100644 public/404.html
create mode 100644 public/422.html
create mode 100644 public/500.html
create mode 100644 public/favicon.ico
create mode 100644 public/images/rails.png
create mode 100644 public/index.html
create mode 100644 public/javascripts/application.js
create mode 100644 public/javascripts/controls.js
create mode 100644 public/javascripts/dragdrop.js
create mode 100644 public/javascripts/effects.js
create mode 100644 public/javascripts/prototype.js
create mode 100644 public/robots.txt
create mode 100644 public/stylesheets/scaffold.css
create mode 100755 script/about
create mode 100755 script/console
create mode 100755 script/dbconsole
create mode 100755 script/destroy
create mode 100755 script/generate
create mode 100755 script/performance/benchmarker
create mode 100755 script/performance/profiler
create mode 100755 script/plugin
create mode 100755 script/runner
create mode 100755 script/server
create mode 100644 test/fixtures/products.yml
create mode 100644 test/functional/products_controller_test.rb
create mode 100644 test/performance/browsing_test.rb
create mode 100644 test/test_helper.rb
create mode 100644 test/unit/helpers/products_helper_test.rb
create mode 100644 test/unit/product_test.rb
delete mode 100644 test_product_server.rb
delete mode 100644 test_products.rb
git push
Counting objects: 93, done.
Compressing objects: 1% (1/77) Compressing objects: 2% (2/77) Compressing objects: 3% (3/77) Compressing objects: 5% (4/77) Compressing objects: 6% (5/77) Compressing objects: 7% (6/77) Compressing objects: 9% (7/77) Compressing objects: 10% (8/77) Compressing objects: 11% (9/77) Compressing objects: 12% (10/77) Compressing objects: 14% (11/77) Compressing objects: 15% (12/77) Compressing objects: 16% (13/77) Compressing objects: 18% (14/77) Compressing objects: 19% (15/77) Compressing objects: 20% (16/77) Compressing objects: 22% (17/77) Compressing objects: 23% (18/77) Compressing objects: 24% (19/77) Compressing objects: 25% (20/77) Compressing objects: 27% (21/77) Compressing objects: 28% (22/77) Compressing objects: 29% (23/77) Compressing objects: 31% (24/77) Compressing objects: 32% (25/77) Compressing objects: 33% (26/77) Compressing objects: 35% (27/77) Compressing objects: 36% (28/77) Compressing objects: 37% (29/77) Compressing objects: 38% (30/77) Compressing objects: 40% (31/77) Compressing objects: 41% (32/77) Compressing objects: 42% (33/77) Compressing objects: 44% (34/77) Compressing objects: 45% (35/77) Compressing objects: 46% (36/77) Compressing objects: 48% (37/77) Compressing objects: 49% (38/77) Compressing objects: 50% (39/77) Compressing objects: 51% (40/77) Compressing objects: 53% (41/77) Compressing objects: 54% (42/77) Compressing objects: 55% (43/77) Compressing objects: 57% (44/77) Compressing objects: 58% (45/77) Compressing objects: 59% (46/77) Compressing objects: 61% (47/77) Compressing objects: 62% (48/77) Compressing objects: 63% (49/77) Compressing objects: 64% (50/77) Compressing objects: 66% (51/77) Compressing objects: 67% (52/77) Compressing objects: 68% (53/77) Compressing objects: 70% (54/77) Compressing objects: 71% (55/77) Compressing objects: 72% (56/77) Compressing objects: 74% (57/77) Compressing objects: 75% (58/77) Compressing objects: 76% (59/77) Compressing objects: 77% (60/77) Compressing objects: 79% (61/77) Compressing objects: 80% (62/77) Compressing objects: 81% (63/77) Compressing objects: 83% (64/77) Compressing objects: 84% (65/77) Compressing objects: 85% (66/77) Compressing objects: 87% (67/77) Compressing objects: 88% (68/77) Compressing objects: 89% (69/77) Compressing objects: 90% (70/77) Compressing objects: 92% (71/77) Compressing objects: 93% (72/77) Compressing objects: 94% (73/77) Compressing objects: 96% (74/77) Compressing objects: 97% (75/77) Compressing objects: 98% (76/77) Compressing objects: 100% (77/77) Compressing objects: 100% (77/77), done.
Writing objects: 1% (1/88) Writing objects: 2% (2/88) Writing objects: 3% (3/88) Writing objects: 4% (4/88) Writing objects: 5% (5/88) Writing objects: 6% (6/88) Writing objects: 7% (7/88) Writing objects: 9% (8/88) Writing objects: 10% (9/88) Writing objects: 11% (10/88) Writing objects: 12% (11/88) Writing objects: 13% (12/88) Writing objects: 14% (13/88) Writing objects: 15% (14/88) Writing objects: 17% (15/88) Writing objects: 18% (16/88) Writing objects: 19% (17/88) Writing objects: 20% (18/88) Writing objects: 22% (20/88) Writing objects: 23% (21/88) Writing objects: 25% (22/88) Writing objects: 26% (23/88) Writing objects: 27% (24/88) Writing objects: 28% (25/88) Writing objects: 29% (26/88) Writing objects: 30% (27/88) Writing objects: 31% (28/88) Writing objects: 32% (29/88) Writing objects: 34% (30/88) Writing objects: 35% (31/88) Writing objects: 36% (32/88) Writing objects: 37% (33/88) Writing objects: 38% (34/88) Writing objects: 39% (35/88) Writing objects: 40% (36/88) Writing objects: 42% (37/88) Writing objects: 43% (38/88) Writing objects: 44% (39/88) Writing objects: 45% (40/88) Writing objects: 46% (41/88) Writing objects: 47% (42/88) Writing objects: 48% (43/88) Writing objects: 50% (44/88) Writing objects: 51% (45/88) Writing objects: 52% (46/88) Writing objects: 53% (47/88) Writing objects: 54% (48/88) Writing objects: 55% (49/88) Writing objects: 56% (50/88) Writing objects: 57% (51/88) Writing objects: 59% (52/88) Writing objects: 60% (53/88) Writing objects: 61% (54/88) Writing objects: 62% (55/88) Writing objects: 63% (56/88) Writing objects: 64% (57/88) Writing objects: 65% (58/88) Writing objects: 67% (59/88) Writing objects: 68% (60/88) Writing objects: 69% (61/88) Writing objects: 70% (62/88) Writing objects: 71% (63/88) Writing objects: 72% (64/88) Writing objects: 73% (65/88) Writing objects: 75% (66/88) Writing objects: 77% (68/88) Writing objects: 78% (69/88) Writing objects: 79% (70/88) Writing objects: 80% (71/88) Writing objects: 81% (72/88) Writing objects: 82% (73/88) Writing objects: 84% (74/88) Writing objects: 85% (75/88) Writing objects: 86% (76/88) Writing objects: 87% (77/88) Writing objects: 88% (78/88) Writing objects: 89% (79/88) Writing objects: 90% (80/88) Writing objects: 92% (81/88) Writing objects: 93% (82/88) Writing objects: 94% (83/88) Writing objects: 95% (84/88) Writing objects: 96% (85/88) Writing objects: 97% (86/88) Writing objects: 98% (87/88) Writing objects: 100% (88/88) Writing objects: 100% (88/88), 84.60 KiB, done.
Total 88 (delta 13), reused 0 (delta 0)
To ssh://sa3ruby@depot.intertwingly.net/~/git/depot.git
4eaa140..8dd1afc master -> master
cap deploy:migrations
* executing `deploy:migrations'
* executing `deploy:update_code'
updating the cached checkout on all servers
executing locally: "git ls-remote sa3ruby@depot.intertwingly.net:git/depot.git master"
* executing "if [ -d /home/sa3ruby/depot.intertwingly.net/shared/cached-copy ]; then cd /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && git fetch origin && git reset --hard 8dd1afcc2636408adcbb662c18528b2347e43d57 && git clean -d -x -f; else git clone sa3ruby@depot.intertwingly.net:git/depot.git /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && cd /home/sa3ruby/depot.intertwingly.net/shared/cached-copy && git checkout -b deploy 8dd1afcc2636408adcbb662c18528b2347e43d57; fi"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
** [depot.intertwingly.net :: out] remote: Counting objects: 93, done.*[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 1% (1/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 2% (2/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 3% (*[K3
** [depot.intertwingly.net :: out] remote: /77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 5% (4/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 6% (5/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 7% (6/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing*[K
** [depot.intertwingly.net :: out] remote: objects: 9% (7/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 10% (8/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 11% (9/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 12% (10/7*[K7
** [depot.intertwingly.net :: out] remote: ) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 14% (11/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 15% (12/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 16% (13/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 18% (14/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 19% (15/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 20% (16/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 22% (17/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 23% (18/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing obje*[Kc
** [depot.intertwingly.net :: out] remote: ts: 24% (19/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 25% (20/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 27% (21/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 28% (22/77)*[K
** [depot.intertwingly.net :: out] remote: *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 29% (23/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 31% (24/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 32% (25/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing o*[Kb
** [depot.intertwingly.net :: out] remote: jects: 33% (26/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 35% (27/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 36% (28/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 37% (29/*[K7
** [depot.intertwingly.net :: out] Unpacking objects: 1% (1/88)
** [depot.intertwingly.net :: out] Unpacking objects: 2% (2/88)
** [depot.intertwingly.net :: out] Unpacking objects: 3% (3/88)
** [depot.intertwingly.net :: out] Unpacking objects: 4% (4/88)
** [depot.intertwingly.net :: out] Unpacking objects: 5% (5/88)
** [depot.intertwingly.net :: out] Unpacking objects: 6% (6/88)
** [depot.intertwingly.net :: out] Unpacking objects: 7% (7/88)
** [depot.intertwingly.net :: out] Unpacking objects: 9% (8/88)
** [depot.intertwingly.net :: out] Unpacking objects: 10% (9/88)
** [depot.intertwingly.net :: out] Unpacking objects: 11% (10/88)
** [depot.intertwingly.net :: out] Unpacking objects: 12% (11/88)
** [depot.intertwingly.net :: out] Unpacking objects: 13% (12/88)
** [depot.intertwingly.net :: out] Unpacking objects: 14% (13/88)
** [depot.intertwingly.net :: out] Unpacking objects: 15% (14/88)
** [depot.intertwingly.net :: out] Unpacking objects: 17% (15/88)
** [depot.intertwingly.net :: out] Unpacking objects: 18% (16/88)
** [depot.intertwingly.net :: out] Unpacking objects: 19% (17/88)
** [depot.intertwingly.net :: out] Unpacking objects: 20% (18/88)
** [depot.intertwingly.net :: out] Unpacking objects: 21% (19/88)
** [depot.intertwingly.net :: out] Unpacking objects: 22% (20/88)
** [depot.intertwingly.net :: out] Unpacking objects: 23% (21/88)
** [depot.intertwingly.net :: out] Unpacking objects: 25% (22/88)
** [depot.intertwingly.net :: out] Unpacking objects: 26% (23/88)
** [depot.intertwingly.net :: out] Unpacking objects: 27% (24/88)
** [depot.intertwingly.net :: out] Unpacking objects: 28% (25/88)
** [depot.intertwingly.net :: out] Unpacking objects: 29% (26/88)
** [depot.intertwingly.net :: out] Unpacking objects: 30% (27/88)
** [depot.intertwingly.net :: out] Unpacking objects: 31% (28/88)
** [depot.intertwingly.net :: out] Unpacking objects: 32% (29/88)
** [depot.intertwingly.net :: out] Unpacking objects: 34% (30/88)
** [depot.intertwingly.net :: out] Unpacking objects: 35% (31/88)
** [depot.intertwingly.net :: out] Unpacking objects: 36% (32/88)
** [depot.intertwingly.net :: out] Unpacking objects: 37% (33/88)
** [depot.intertwingly.net :: out] Unpacking objects: 38% (34/88)
** [depot.intertwingly.net :: out] Unpacking objects: 39% (35/88)
** [depot.intertwingly.net :: out] Unpacking objects: 40% (36/88)
** [depot.intertwingly.net :: out] Unpacking objects: 42% (37/88)
** [depot.intertwingly.net :: out] Unpacking objects: 43% (38/88)
** [depot.intertwingly.net :: out] Unpacking objects: 44% (39/88)
** [depot.intertwingly.net :: out] Unpacking objects: 45% (40/88)
** [depot.intertwingly.net :: out] Unpacking objects: 46% (41/88)
** [depot.intertwingly.net :: out] Unpacking objects: 47% (42/88)
** [depot.intertwingly.net :: out] Unpacking objects: 48% (43/88)
** [depot.intertwingly.net :: out] Unpacking objects: 50% (44/88)
** [depot.intertwingly.net :: out] Unpacking objects: 51% (45/88)
** [depot.intertwingly.net :: out] remote: 7) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 38% (30/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 40% (31/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 41% (32/77) *[K
** [depot.intertwingly.net :: out] remote: Compressin*[Kg
** [depot.intertwingly.net :: out] remote: objects: 42% (33/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 44% (34/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 45% (35/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 46% (*[K3
** [depot.intertwingly.net :: out] remote: 6/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 48% (37/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 49% (38/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 50% (39/77) *[K
** [depot.intertwingly.net :: out] remote: Compres*[Ks
** [depot.intertwingly.net :: out] remote: ing objects: 51% (40/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 53% (41/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 54% (42/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 55*[K%
** [depot.intertwingly.net :: out] remote: (43/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 57% (44/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 58% (45/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 59% (46/77) *[K
** [depot.intertwingly.net :: out] remote: Comp*[Kr
** [depot.intertwingly.net :: out] remote: essing objects: 61% (47/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 62% (48/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 63% (49/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: *[K
** [depot.intertwingly.net :: out] remote: 64% (50/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 66% (51/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 67% (52/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 68% (53/77) *[K
** [depot.intertwingly.net :: out] remote: C*[Ko
** [depot.intertwingly.net :: out] remote: mpressing objects: 70% (54/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 71% (55/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 72% (56/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing object*[Ks
** [depot.intertwingly.net :: out] remote: : 74% (57/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 75% (58/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 76% (59/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 77% (60/77) *[K
** [depot.intertwingly.net :: out] remote:
** [depot.intertwingly.net :: out] remote: Compressing objects: 79% (61/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 80% (62/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 81% (63/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing obj*[Ke
** [depot.intertwingly.net :: out] remote: cts: 83% (64/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 84% (65/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 85% (66/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 87% (67/77*[K)
** [depot.intertwingly.net :: out] Unpacking objects: 52% (46/88)
** [depot.intertwingly.net :: out] Unpacking objects: 53% (47/88)
** [depot.intertwingly.net :: out] Unpacking objects: 54% (48/88)
** [depot.intertwingly.net :: out] Unpacking objects: 55% (49/88)
** [depot.intertwingly.net :: out] Unpacking objects: 56% (50/88)
** [depot.intertwingly.net :: out] Unpacking objects: 57% (51/88)
** [depot.intertwingly.net :: out] Unpacking objects: 59% (52/88)
** [depot.intertwingly.net :: out] Unpacking objects: 60% (53/88)
** [depot.intertwingly.net :: out] Unpacking objects: 61% (54/88)
** [depot.intertwingly.net :: out] remote: *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 88% (68/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 89% (69/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 90% (70/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing *[Ko
** [depot.intertwingly.net :: out] remote: bjects: 92% (71/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 93% (72/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 94% (73/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 96% (74*[K/
** [depot.intertwingly.net :: out] remote: 77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 97% (75/77) *[K
** [depot.intertwingly.net :: out] remote: Compressing objects: 98% (76/77) *[K
** [depot.intertwingly.net :: out] Unpacking objects: 62% (55/88)
** [depot.intertwingly.net :: out] Unpacking objects: 63% (56/88)
** [depot.intertwingly.net :: out] Unpacking objects: 64% (57/88)
** [depot.intertwingly.net :: out] Unpacking objects: 65% (58/88)
** [depot.intertwingly.net :: out] remote: Compressing objects: 100% (77/77) *[K
** [depot.intertwingly.net :: out] remote: Compressi*[Kn
** [depot.intertwingly.net :: out] Unpacking objects: 67% (59/88)
** [depot.intertwingly.net :: out] remote: g objects: 100% (77/77), done.*[K
** [depot.intertwingly.net :: out] Unpacking objects: 68% (60/88)
** [depot.intertwingly.net :: out] Unpacking objects: 69% (61/88)
** [depot.intertwingly.net :: out] Unpacking objects: 70% (62/88)
** [depot.intertwingly.net :: out] Unpacking objects: 71% (63/88)
** [depot.intertwingly.net :: out] Unpacking objects: 72% (64/88)
** [depot.intertwingly.net :: out] Unpacking objects: 73% (65/88)
** [depot.intertwingly.net :: out] Unpacking objects: 75% (66/88)
** [depot.intertwingly.net :: out] Unpacking objects: 76% (67/88)
** [depot.intertwingly.net :: out] Unpacking objects: 77% (68/88)
** [depot.intertwingly.net :: out] Unpacking objects: 78% (69/88)
** [depot.intertwingly.net :: out] Unpacking objects: 79% (70/88)
** [depot.intertwingly.net :: out] Unpacking objects: 80% (71/88)
** [depot.intertwingly.net :: out] Unpacking objects: 81% (72/88)
** [depot.intertwingly.net :: out] Unpacking objects: 82% (73/88)
** [depot.intertwingly.net :: out] Unpacking objects: 84% (74/88)
** [depot.intertwingly.net :: out] Unpacking objects: 85% (75/88)
** [depot.intertwingly.net :: out] Unpacking objects: 86% (76/88)
** [depot.intertwingly.net :: out] Unpacking objects: 87% (77/88)
** [depot.intertwingly.net :: out] Unpacking objects: 88% (78/88)
** [depot.intertwingly.net :: out] Unpacking objects: 89% (79/88)
** [depot.intertwingly.net :: out] Unpacking objects: 90% (80/88)
** [depot.intertwingly.net :: out] Unpacking objects: 92% (81/88)
** [depot.intertwingly.net :: out] Unpacking objects: 93% (82/88)
** [depot.intertwingly.net :: out] Unpacking objects: 94% (83/88)
** [depot.intertwingly.net :: out] Unpacking objects: 95% (84/88)
** [depot.intertwingly.net :: out] Unpacking objects: 96% (85/88)
** [depot.intertwingly.net :: out] Unpacking objects: 97% (86/88)
** [depot.intertwingly.net :: out] Unpacking objects: 98% (87/88)
** [depot.intertwingly.net :: out] Unpacking objects: 100% (88/88)
** [depot.intertwingly.net :: out] Unpacking objects: 100% (88/88), done.
** [depot.intertwingly.net :: out] remote: Total 88 (delta 13), reused 0 (delta 0)*[K
** [depot.intertwingly.net :: out] From sa3ruby@depot.intertwingly.net:git/depot
** 4eaa140..8dd1afc master -> origin/master
** [depot.intertwingly.net :: out] HEAD is now at 8dd1afc convert to Rails!
command finished
copying the cached version to /home/sa3ruby/depot.intertwingly.net/releases/20090711163952
* executing "cp -RPp /home/sa3ruby/depot.intertwingly.net/shared/cached-copy /home/sa3ruby/depot.intertwingly.net/releases/20090711163952 && (echo 8dd1afcc2636408adcbb662c18528b2347e43d57 > /home/sa3ruby/depot.intertwingly.net/releases/20090711163952/REVISION)"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing `deploy:finalize_update'
* executing "chmod -R g+w /home/sa3ruby/depot.intertwingly.net/releases/20090711163952"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
triggering after callbacks for `deploy:update_code'
* executing `deploy:set_db_path'
* executing "sed -i 's|db/production|/home/sa3ruby/depot.intertwingly.net/depot|' /home/sa3ruby/depot.intertwingly.net/releases/20090711163952/config/database.yml"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing `deploy:set_gem_home'
* executing "sed -i '1iENV[%(GEM_HOME)]=%(/home/sa3ruby/.gems)\\n' /home/sa3ruby/depot.intertwingly.net/releases/20090711163952/config/environment.rb"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing `deploy:migrate'
* executing "ls -xt /home/sa3ruby/depot.intertwingly.net/releases"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "cd /home/sa3ruby/depot.intertwingly.net/releases/20090711163952; rake RAILS_ENV=production db:migrate"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
** [out :: depot.intertwingly.net] (in /home/sa3ruby/depot.intertwingly.net/releases/20090711163952)
** [out :: depot.intertwingly.net] == CreateProducts: migrating =================================================
** [out :: depot.intertwingly.net] -- create_table(:products)
** [out :: depot.intertwingly.net] -> 0.0884s
** [out :: depot.intertwingly.net] == CreateProducts: migrated (0.0886s) ========================================
** [out :: depot.intertwingly.net]
command finished
* executing `deploy:migrate'
* executing "cd /home/sa3ruby/depot.intertwingly.net/releases/20090711163952; rake RAILS_ENV=production db:migrate"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
** [out :: depot.intertwingly.net] (in /home/sa3ruby/depot.intertwingly.net/releases/20090711163952)
command finished
* executing `deploy:symlink'
* executing "rm -f /home/sa3ruby/depot.intertwingly.net/current && ln -s /home/sa3ruby/depot.intertwingly.net/releases/20090711163952 /home/sa3ruby/depot.intertwingly.net/current"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
triggering after callbacks for `deploy:symlink'
* executing `deploy:restart'
* executing "mkdir -p /home/sa3ruby/depot.intertwingly.net/current/tmp"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "touch /home/sa3ruby/depot.intertwingly.net/current/tmp/restart.txt"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
triggering after callbacks for `deploy:restart'
* executing `deploy:cleanup'
*** no old releases to clean up
* executing `deploy:update_crontab'
* executing "cd /home/sa3ruby/depot.intertwingly.net/current && whenever --update-crontab depot"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
** [out :: depot.intertwingly.net] [write] crontab file updated
command finished
* executing `deploy:restart'
* executing "mkdir -p /home/sa3ruby/depot.intertwingly.net/current/tmp"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
* executing "touch /home/sa3ruby/depot.intertwingly.net/current/tmp/restart.txt"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
command finished
triggering after callbacks for `deploy:restart'
* executing `deploy:cleanup'
*** no old releases to clean up
* executing `deploy:update_crontab'
* executing "cd /home/sa3ruby/depot.intertwingly.net/current && whenever --update-crontab depot"
servers: ["depot.intertwingly.net"]
[depot.intertwingly.net] executing command
** [out :: depot.intertwingly.net] [write] crontab file updated
command finished
See this live.
get http://depot.intertwingly.net/products