On the Rails: Set Up Ruby, Rails and PostgreSQL on Ubuntu 10.04

May 31st, 2010 Comments off

Hello again. Today we are going to be installing Ruby (1.8.7 patchlevel 174), RubyGems (1.3.7), Ruby on Rails (2.3.8) and PostgreSQL (8.4) on Ubuntu Desktop 32-bit 10.04 (Lucid Lynx) LTS (Long-Term Support release). This runthrough assumes a clean install of Lucid, with all available updates/upgrades applied.

We are going to be more or less following the instructions found in a gist from Marc Chung, with only minor additions and corrections where appropriate. We will be using the wonderful Ruby Version Manager (rvm) to install Ruby and RubyGems.

Install Packages

First, let’s install the Ubuntu packages we’ll need to install rvm, Ruby and RubyGems. While we’re at it, we’ll also install our database server (PostgreSQL) and two packages (libxslt1-dev and libpq-dev) we’ll need later to build the native extensions to two of our gems (nokogiri and pg, respectively). Run the following command from a Terminal window:

sudo apt-get install curl git-core bison build-essential zlib1g-dev libssl-dev libreadline6-dev libxml2-dev autoconf libxslt1-dev libpq-dev postgresql

Type your user password when prompted. The first two packages are needed to get rvm; the rest, except for the last three, are needed to build Ruby and RubyGems.

Install rvm

Now, let’s download and install rvm. To accomplish this, run the following commands in order:

curl http://rvm.beginrescueend.com/releases/rvm-install-head >rvm-install-head
chmod a+x rvm-install-head
./rvm-install-head

This command sequence saves a local copy of an installer script from the rvm site and runs it. The installer script, in turn, grabs rvm from a repository on GitHub and installs it.

You’ll see some messages when rvm is done installing, telling you to modify your ~/.bashrc file. I modified mine as follows. I opened up the file in pico:

pico ~/.bashrc

I commented out line 6:

[ -z "$PS1" ] && return

I inserted a new line 7:

if [[ ! -z "$PS1" ]] ; then

I then went to the end of the file and appended these two lines:

[[ -s $HOME/.rvm/scripts/rvm ]] && source $HOME/.rvm/scripts/rvm
fi

I saved the file with Ctrl-O, exited pico with Ctrl-X, and then closed and reopened my Terminal.

Install Ruby and RubyGems

Next, we’ll download and install the particular “ruby” (singular form of “rubies”) that we mentioned above, along with the RubyGems gem management software and two actual gems — Rake and RDoc. Run the following:

rvm install 1.8.7-p174
rvm use 1.8.7-p174
rvm --default 1.8.7-p174

The second command tells rvm that the ruby we just installed is, in fact, the ruby we want to use and the one that will respond when we type “ruby” on the command line. The third sets that ruby as the default, so we don’t have to type “rvm use <whatever>” every time we open a new Terminal, which is just an unnecessary step if we only have one ruby installed.

Trust, But Verify

Before we go any further, let’s verify that we have, in fact, installed the correct versions of ruby, rubygems, rake and rdoc. The following command/response sequence should demonstrate that this is true (The $ represents the prompt):

$ ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-linux]
$ gem -v
1.3.7
$ gem list

*** LOCAL GEMS ***

rake (0.8.7)
rdoc (2.5.8)

Assuming we’re all good, let’s continue.

Install Bundler and Rails

Now, we’re ready to install Bundler and then the Ruby on Rails web application development framework. What’s Bundler, you ask? Good question. It’s a system to help you manage the gems required by your applications in a shared production environment (when you have more than one application running on a single server).

So, let’s run the following:

gem install bundler
gem install rails

Again, let’s verify:

$ rails -v
Rails 2.3.8
$ gem list

*** LOCAL GEMS ***

actionmailer (2.3.8)
actionpack (2.3.8)
activerecord (2.3.8)
activeresource (2.3.8)
activesupport (2.3.8)
bundler (0.9.25)
rack (1.1.0)
rails (2.3.8)
rake (0.8.7)
rdoc (2.5.8)

Theoretically, the above list shows all the gems that we’ll ever need to install locally with the “gem install” command to develop an application using Rails 2.3.8. Any and all further gems required by our app will be installed for us (locally, in production, and everywhere else our app might live) by Bundler. We’ll tell Bundler about all of these other gems in a moment.

Create Rails App, Insert Gemfile

Now, let’s set up a new default Rails app, specifying PostgreSQL as the database:

mkdir workspace
cd workspace
rails rails238test -d postgresql
cd rails238test

At this point, we’re ready to add a Gemfile to the project. As its name might suggest, this file lists all of the gems our application needs to run — on our local development machine, on our production server, and everywhere in between. Paste the following into a new text file in gedit, and save it to your project’s root (~/workspace/rails238test) under the name “Gemfile”:

source "http://rubygems.org"

gem "rails", "2.3.8"
gem "pg", "0.9.0"
gem "capistrano"
gem "heroku"

gem "rack", "1.1.0"
gem "clearance"
gem "fastercsv"

group :test do
  gem "rspec"
  gem "rspec-rails", "1.3.2"
  gem "faker"
  gem "database_cleaner"
  gem "capybara"
  gem "cucumber"
  gem "cucumber-rails"
  gem "test-unit"

  gem "factory_girl"
  gem "formtastic"
  gem "email_spec"
end

As you might guess from taking a gander at this list of gems, we’re going to be getting into all kinds of cool new things here. We’re installing the PostgreSQL database adapter gem, which is obviously necessary and not new to us. We’re also going to be using Capistrano to deploy our app — again, not new.

But, other than those two, everything else on the list is something we haven’t dealt with before. We’ll be getting into unit testing with Test::Unit and Factory Girl, behavior-driven development (BDD) with RSpec and Cucumber, auto-generation of dummy data with Faker, integration testing with Capybara, and more. We’ll also be deploying our app to that great gig Ruby application platform in the sky — Heroku!

Bundle

Let’s set this sucker off. Run the following:

bundle install

This command will download and install all of the gems specified in our Gemfile, along with their dependencies. Four of them require native extensions to be built. Of those, two require the development libraries we installed earlier as Ubuntu packages.

Assuming all went well, our app now has a “bundle” of joy gems associated with it.

Replace Database Config

Next, we’ll replace the contents of our ~/workspace/rails238test/config/database.yml file with the following:

development:
  adapter: postgresql
  encoding: utf8
  database: rails238test_development
  username: postgres
  password:
  host: localhost

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test: &TEST
  adapter: postgresql
  encoding: utf8
  database: rails238test_test
  username: postgres
  password:
  host: localhost

Set Up RSpec, Cucumber & Capybara

From your project root (~/workspace/rails238test), run the following:

$ ./script/generate rspec
Configuring rspec and rspec-rails gems in config/environments/test.rb ...

      exists  lib/tasks
      create  lib/tasks/rspec.rake
      create  script/autospec
      create  script/spec
      create  spec
      create  spec/rcov.opts
      create  spec/spec.opts
      create  spec/spec_helper.rb
$ ./script/generate cucumber --rspec --capybara
       force  config/database.yml
      create  config/cucumber.yml
      create  config/environments/cucumber.rb
      create  script/cucumber
      create  features/step_definitions
      create  features/step_definitions/web_steps.rb
      create  features/support
      create  features/support/paths.rb
      create  features/support/env.rb
      exists  lib/tasks
      create  lib/tasks/cucumber.rake

Those two steps bootstrap our Rails app for use with RSpec, Cucumber and Capybara. They create some files, and add a couple lines to our database.yml file.

Generate a Cucumber Feature and a Scaffold

Now, let’s run the following (again, from our project root):

$ ./script/generate feature book title:string author:string publisher:string copyright_year:string isbn:string
      exists  features/step_definitions
      create  features/manage_books.feature
      create  features/step_definitions/book_steps.rb
$ ./script/generate scaffold book title:string author:string publisher:string copyright_year:string isbn:string
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/books
      exists  app/views/layouts/
      exists  test/functional/
      exists  test/unit/
      create  test/unit/helpers/
      exists  public/stylesheets/
      create  app/views/books/index.html.erb
      create  app/views/books/show.html.erb
      create  app/views/books/new.html.erb
      create  app/views/books/edit.html.erb
      create  app/views/layouts/books.html.erb
      create  public/stylesheets/scaffold.css
      create  app/controllers/books_controller.rb
      create  test/functional/books_controller_test.rb
      create  app/helpers/books_helper.rb
      create  test/unit/helpers/books_helper_test.rb
       route  map.resources :books
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/book.rb
      create    test/unit/book_test.rb
      create    test/fixtures/books.yml
      create    db/migrate
      create    db/migrate/20100531032247_create_books.rb

All this did was auto-generate a feature, along with a scaffold matching that feature. In our case, the feature we want to implement is “basic CRUD functions for data on books”.  The scaffold we generated implements that feature (that part should look familiar if you’ve seen my earlier series of posts on getting started with Rails).

Create Database & Schema

Now, we need to get our database and schema created, so that we can test what we just generated. Before we do this, we need to change one of our PostgreSQL config files to allow Rails to connect to it. Run the following:

sudo pico /etc/postgresql/8.4/main/pg_hba.conf

Scroll down to the bottom of the file, and you’ll see a section that looks like this:

# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD

# "local" is for Unix domain socket connections only
local   all         all                               ident
# IPv4 local connections:
host    all         all         127.0.0.1/32          md5
# IPv6 local connections:
host    all         all         ::1/128               md5

Make it look like this:

# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD

# "local" is for Unix domain socket connections only
local   all         all                               trust
# IPv4 local connections:
host    all         all         127.0.0.1/32          trust
# IPv6 local connections:
host    all         all         ::1/128               trust

It took me a while, but I finally found an old Ruby on Rails wiki page that explains why this step is needed, and why the solution we applied in a previous post (changing only the “local” line of that section) doesn’t work.

It turns out that Rails connects to the local PostgreSQL server via TCP sockets, not UNIX domain sockets. (So why did our previous solution work before? It’s a mystery to me at the moment. Maybe I’ll discover the answer and write about it in a future post.)

This time, we’re trying to connect to the server as, and create a database for, the PostgreSQL user “postgres” — the only one that exists right now, which doesn’t have a password set.

The change we just made opens up the ability for any application, running locally as any UNIX user, connecting on any UNIX domain socket or TCP socket (using IPv4 or v6), to connect to the PostgreSQL server as any PostgreSQL user, without a password.

This level of security obviously will not do in a staging or production environment, but it will be fine for our local development purposes.

So, let’s save the config file with Ctrl-O, exit pico with Ctrl-X, and tell PostgreSQL to reload its configuration files with the following command:

sudo su postgres -c "/usr/lib/postgresql/8.4/bin/pg_ctl reload -D /var/lib/postgresql/8.4/main"

Now, let’s create our database:

$ rake db:create --trace
(in /home/jeremy/workspace/rails238test)
** Invoke db:create (first_time)
** Invoke db:load_config (first_time)
** Invoke rails_env (first_time)
** Execute rails_env
** Execute db:load_config
** Execute db:create

And our schema:

$ rake db:migrate --trace
(in /home/jeremy/workspace/rails238test)
** Invoke db:migrate (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute db:migrate
==  CreateBooks: migrating ====================================================
-- create_table(:books)
NOTICE:  CREATE TABLE will create implicit sequence "books_id_seq" for serial column "books.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "books_pkey" for table "books"
   -> 0.1953s
==  CreateBooks: migrated (0.1960s) ===========================================

** Invoke db:schema:dump (first_time)
** Invoke environment
** Execute db:schema:dump

Test Models

First, let’s test our specs:

$ rake spec --trace
(in /home/jeremy/workspace/rails238test)
** Invoke spec (first_time)
** Invoke db:test:prepare (first_time)
** Invoke db:abort_if_pending_migrations (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute db:abort_if_pending_migrations
** Execute db:test:prepare
** Invoke db:test:load (first_time)
** Invoke db:test:purge (first_time)
** Invoke environment
** Execute db:test:purge
** Execute db:test:load
** Invoke db:schema:load (first_time)
** Invoke environment
** Execute db:schema:load
NOTICE:  CREATE TABLE will create implicit sequence "books_id_seq" for serial column "books.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "books_pkey" for table "books"
** Execute spec

And now, let’s test our features:

$ rake cucumber --trace
(in /home/jeremy/workspace/rails238test)
** Invoke cucumber (first_time)
** Invoke cucumber:ok (first_time)
** Invoke db:test:prepare (first_time)
** Invoke db:abort_if_pending_migrations (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute db:abort_if_pending_migrations
** Execute db:test:prepare
** Invoke db:test:load (first_time)
** Invoke db:test:purge (first_time)
** Invoke environment
** Execute db:test:purge
** Execute db:test:load
** Invoke db:schema:load (first_time)
** Invoke environment
** Execute db:schema:load
NOTICE:  CREATE TABLE will create implicit sequence "books_id_seq" for serial column "books.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "books_pkey" for table "books"
** Execute cucumber:ok
/home/jeremy/.rvm/rubies/ruby-1.8.7-p174/bin/ruby -I "/home/jeremy/.rvm/gems/ruby-1.8.7-p174/gems/cucumber-0.7.3/lib:lib" "/home/jeremy/.rvm/gems/ruby-1.8.7-p174/gems/cucumber-0.7.3/bin/cucumber"  --profile default
Using the default profile...
...............

2 scenarios (2 passed)
15 steps (15 passed)
0m1.003s
** Execute cucumber

Create Local Git Repo, Prepare for Heroku

So, now, let’s Git-ify this thing (again, make sure you’re in ~/workspace/rails238test). First, let’s create a .gitignore file so that we can exclude certain files from our Git repository:

cat<<EOF >.gitignore
> .bundle
> .gitignore
> EOF

We just excluded our .bundle directory, and the .gitignore file itself. Now, let’s run the following commands in order:

git init
git config --global user.email <your_email_address>
git config --global user.name "<your_name>"
git add .
git commit -m 'first commit'

Our app is now ready to go up to Heroku. Before we can do that, we’ll need an account on Heroku. Go here to sign up for a free account.

Once we’ve done that, we’ll need to generate an SSH keypair:

ssh-keygen -C "<your_email_address>" -t rsa

Create App on Heroku, Deploy

OK. Let’s get this baby onto Heroku! First, we’ll need to create the app on Heroku:

$ heroku create <some_unique_identifier>-rails238test
Enter your Heroku credentials.
Email: <your_email_address>
Password: <your_heroku_password>
Uploading ssh public key /home/jeremy/.ssh/id_rsa.pub
Creating <some_unique_identifier>-rails238test.... done
Created http://<some_unique_identifier>-rails238test.heroku.com/ | git@heroku.com:<some_unique_identifier>-rails238test.git

Next, we’ll add our remote destination to our git repo, and push our code up there:

$ git remote add heroku git@heroku.com:<some_unique_identifier>-rails238test.git
$ git push heroku master
The authenticity of host 'heroku.com (174.129.212.2)' can't be established.
RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'heroku.com,174.129.212.2' (RSA) to the list of known hosts.
Counting objects: 116, done.
Compressing objects: 100% (100/100), done.
Writing objects: 100% (116/116), 97.84 KiB, done.
Total 116 (delta 10), reused 0 (delta 0)

-----> Heroku receiving push
-----> Gemfile detected, running Bundler
-----> Bundler works best on the Bamboo stack.  Please migrate your app:

http://docs.heroku.com/bamboo

       Unresolved dependencies detected; Installing...
       Fetching source index from http://rubygems.org/
       Resolving dependencies
       Installing actionmailer (2.3.8) from rubygems repository at http://rubygems.org/
... the rest of our gems ...
       Your bundle is complete!
       Locking environment
-----> Rails app detected
       Compiled slug size is 11.2MB
-----> Launching......... done
       http://<some_unique_identifier>-rails238test.heroku.com deployed to Heroku

To git@heroku.com:<some_unique_identifier>-rails238test.git
 * [new branch]      master -> master

Hmm. Looks like we should have specified the Bamboo stack when we created the app on Heroku. But that’s OK; we can migrate to another stack. Before we do that, let’s set up our app’s database on Heroku:

$ heroku rake db:migrate
(in /disk1/home/slugs/<some_other_unique_identifying_string>/mnt)
==  CreateBooks: migrating ====================================================
-- create_table(:books)
   -> 0.0163s
==  CreateBooks: migrated (0.0164s) ===========================================

It’s now time for the moment of truth. Let’s point our browser at <some_unique_identifier>-rails238test.heroku.com/books, and see what we get!

If you see a page that says “Listing books” at the top, you’re in business!

Though we probably don’t need to (given that we’re just running our simple “books” CRUD app), let’s migrate to the Bamboo stack like Heroku says:

$ heroku stack:migrate bamboo-ree-1.8.7
-----> Preparing to migrate <some_unique_identifier>-rails238test
       aspen-mri-1.8.6 -> bamboo-ree-1.8.7

       NOTE: You must specify ALL gems (including Rails) in manifest

       Please read the migration guide:

http://docs.heroku.com/bamboo

-----> Migration prepared.
       Run 'git push heroku master' to execute migration.
$ git push heroku master
Warning: Permanently added the RSA host key for IP address '75.101.145.87' to the list of known hosts.
Everything up-to-date
$ heroku stack
* aspen-mri-1.8.6
  bamboo-ree-1.8.7 (beta) (prepared, will migrate on next git push)
  bamboo-mri-1.9.1 (beta)

It won’t migrate unless it actually has some code changes to push up. So, we’ll create a dummy commit and push that:

$ echo >> Rakefile && git commit -a -m "migrating to bamboo stack"
[master 48624c5] migrating to bamboo stack
 1 files changed, 1 insertions(+), 0 deletions(-)
$ git push heroku master
Warning: Permanently added the RSA host key for IP address '75.101.163.44' to the list of known hosts.
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 305 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)

-----> Heroku receiving push
-----> Migrating from aspen-mri-1.8.6 to bamboo-ree-1.8.7

-----> Gemfile detected, running Bundler
       Unresolved dependencies detected; Installing...
       Fetching source index from http://rubygems.org/
       Using rake (0.8.7) from system gems
       Installing activesupport (2.3.8) from rubygems repository at http://rubygems.org/
... the rest of our gems ...
       Your bundle is complete! Use `bundle show gemname` to see where a bundled gem is installed.
       Locking environment
-----> Rails app detected
       Compiled slug size is 11.2MB
-----> Launching............ done
       http://<some_unique_identifier>-rails238test.heroku.com deployed to Heroku

-----> Migration complete, your app is now running on bamboo-ree-1.8.7

To git@heroku.com:<some_unique_identifier>-rails238test.git
   87aa8d5..48624c5  master -> master

Conclusion

Success! We’re done!

Of course, we’ve just taken “one small step for [a] man, [but] one giant leap for Web-dev-kind.” There’s plenty more fun to be had exploring Ruby, Rails, TDD and BDD, and running apps on PaaS (Platform-as-a-Service) clouds like Heroku. I’ll be sure to chronicle more of my adventures here. Until next time, happy coding!

Categories: On the Ground Tags:

On the Rails: Capify and Deploy!

January 10th, 2010 Comments off

Ready to get our app onto that Rackspace Cloud Server? Me too; let’s do it!

Install

First, on our Mac, we need to install Capistrano, which will deploy our app for us once we have it set up. It’s a Ruby gem. Do the following to get it:

sudo gem sources -a http://gems.github.com/
sudo gem install capistrano

Capify

Now, let’s run these commands from our home directory:

cd workspace/railstest
capify .

Capistrano will add two files to our app, Capfile and config/deploy.rb. Let’s edit config/deploy.rb:

pico config/deploy.rb

Here is one place we can look for help in figuring out what to put in this file so that Capistrano will know where and how to deploy our app.

Below is what our config/deploy.rb file ends up looking like:

set :application, "railstest"
set :repository,  "git@github.com:your_github_username/railstest.git"

# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
set :deploy_to, "/var/local/#{application}"

# If you aren't using Subversion to manage your source code, specify
# your SCM below:
set :scm, :git
# Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`

set :deploy_via, :remote_cache
set :branch, "master"

set :passenger_conf, true
set :user, "app" # Login as?
set :runner, "app" # Run ./script as?
set :use_sudo, false

set :domain, "rails.example.com"

role :web, domain                          # Your HTTP server, Apache/etc
role :app, domain                          # This may be the same as your `Web` server
role :db,  domain, :primary => true        # This is where Rails migrations will run
#role :db,  "your slave db-server here"

set :rails_env, "production"

# If you are using Passenger mod_rails uncomment this:
# if you're still using the script/reapear helper you will need
# these http://github.com/rails/irs_process_scripts

namespace :deploy do
  task :start do ; end
  task :stop do ; end
  task :restart, :roles => :app, :except => { :no_release => true } do
    run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
  end
end

Deploy

Now, we’re ready to run “cap” and actually do some things. :) Let’s follow some steps in the handy guide referenced above:

cap deploy:setup
cap deploy:check
cap deploy:update

Note: The “update” task failed for me the first time I tried it, and I think the “Known Hosts List” section at the bottom of this page explains why. I solved the problem by SSHing into the server and doing the following:

su app
git ls-remote git@github.com:your_github_username/railstest.git master

You should see something like:

The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)?

Type “yes”, and press Enter, and you should see something like:

Warning: Permanently added 'github.com,207.97.227.239' (RSA) to the list of known hosts.
502d44c77a98c812eff27e97d542f47ed891fad1        refs/heads/master

I tried “cap deploy:update” again from my Mac and it succeeded.

OK, let’s continue. We’re already on the server, in a shell as the “app” user, so let’s try this:

cd /var/local/railstest/current
rake RAILS_ENV=production db:schema:load

Note: This one didn’t work for me at first either. I found an explanation here, which seems to line up with what the PostgreSQL manual says. Evidently, the default is for postgresql to only allow UNIX socket connections as a database user with the same name as the operating system user. In our case, we’re running rake as “app”, and rake is trying to connect to postgresql as “railstest”, which isn’t going to work under the default setup. So, let’s change the setup. Type “exit” to get back to your root shell. As root, do this:

pico /etc/postgresql/8.4/main/pg_hba.conf

Comment out the line that says:

local all all ident sameuser

and add a line right below it that says:

local all all trust

Then, run:

sudo su postgres -c "/usr/lib/postgresql/8.4/bin/pg_ctl reload -D /var/lib/postgresql/8.4/main"

to have postgresql reload the config file we just edited. Now, let’s try this step again:

su app
cd /var/local/railstest/current
rake RAILS_ENV=production db:schema:load

It works! Next, do:

./script/console production
app.get("/")

I got a 404 from this, which apparently isn’t a bad thing in our case, because we haven’t configured our app to return anything from that location in production mode.

Next, opening up a web browser on your Mac and browsing to:

http://rails.example.com/javascripts/prototype.js

should return what you would expect.

So, now that we’ve checked every step of the process, let’s run the whole she-bang. In your Mac Terminal, do:

cap deploy

If it works, then we are now at the moment of truth. In our web browser, let’s browse to:

http://rails.example.com/books

Our app should come up! Success! We’re done! WOO-HOO!!!

Conclusion

Thanks for joining me on this journey (if there’s been anyone out there reading this). It’s actually been quite a ride for me, a PHP/MySQL monkey by trade. I hope you’ve had as much fun learning about this stuff as I have. No doubt there’s a better way to do some of the things described in this series. I’ll probably update some of what I’ve written with better methods in future posts. And I’m sure there will be plenty of all-new adventures “On the Rails” in the future. If you have something to share, please feel free to drop me a line. Thanks again, and see you down the road!

Categories: On the Ground Tags:

On the Rails: Bootstrap a Rackspace Cloud Server

January 8th, 2010 Comments off

I’m going to assume you already have a Rackspace Cloud account. (If you don’t, I’ll leave it to you to rectify the situation.)

Create a Server

Follow these steps to create a Rackspace Cloud Server:

  1. Log in to the control panel at manage.rackspacecloud.com.
  2. Click on Hosting in the left nav bar, then Cloud Servers.
  3. Click Add New Server.
  4. Now, it’s time to pick how much RAM and storage space you want (and how much you want to pay per hour). The default, 256 MB RAM and 10 GB storage at $0.015 an hour, is fine for our purposes.
  5. Scroll down and give your server a name, like, oh, I don’t know, “Fred” or “Rails-Test” or something.
  6. Next, choose your favorite flavor of ice cream operating system. I like the Ubuntu LTS (Long-Term Support) releases. Yum. No, wait, I mean apt-get. :-P
  7. Finally, click Add Cloud Server.

While it is building your server according to your specifications, the control panel will give you an IP address and a root password. This information will also be emailed to the primary contact email address associated with your account. Be sure to keep it in a safe place.

Once your server is ready, you can SSH into it. In a Terminal, issue the following command:

ssh root@<your_server_IP_address>

Enter your root password when prompted.

Bootstrap That Baby

Marc Chung (formerly of OpenRain, now of redPear) has published, as a GitHub repo, a collection of handy-dandy shell scripts to aid us in bootstrapping a Rails server. Go here and start to follow the instructions in the README. Update and upgrade Ubuntu, install Git, and clone the Git repo to get the scripts.

Set Up Password-less Authentication

Before we start running scripts, we ought to make it so that we can connect to our remote server from our local Mac without having to type in a password, while at the same time ensuring that no one besides us can connect in this way. (This is necessary for the deployment process to work, as we’ll see in our next episode.) We’ll achieve this by adding the public key we generated earlier on our Mac to an “authorized_keys” file on the remote server. (reference)

  1. On the Mac, run “cat ~/.ssh/id_rsa.pub” and copy the result to the clipboard.
  2. On the server, run “pico ~/bootstrap/config/root_authorized_keys”. Paste the key into this file and save it (Ctrl-O).
  3. On the server, run “pico ~/bootstrap/config/default_authorized_keys”. Paste the key into this file and save it (Ctrl-O).

Edit/Run Scripts

On the server, from the ~/bootstrap/slicehost directory, we are going to be running these scripts in order:

00-core.sh
05-ruby.sh
10-misc.sh
15-postgresql.sh
25-apache.sh
36-passenger.sh
40-rails-apps.sh

You can run the core, ruby, and misc scripts as written.

Before running the postgresql script, you should edit it to customize one of the config options. Namely, edit the “add addresses” block in that file. Change the IP addresses to match the machines from which you’ll be remotely connecting to your PostgreSQL database server, if any. If there are none, you can delete this block.

When running the postgresql script, you have to provide it a password for your database’s admin user. We’ll set a temporary environment variable for this, at the same time we run the script, as shown in Marc’s instructions:

PGPASSWORD='clark_kent_is_superman' sh ./15-postgresql.sh

Be sure to replace the value given for the password with your own value.

We’ll then run a separate block of script, similar to what’s shown in Marc’s instructions, to create a normal user for our database, and then the database itself:

cat > /tmp/postgres.sql <<EOF
CREATE ROLE railstest
  LOGIN ENCRYPTED PASSWORD 'batman_is_bruce_wayne'
  INHERIT CREATEDB;
CREATE DATABASE railstest
  OWNER = railstest;
EOF
sudo su postgres -c psql template1 < /tmp/postgres.sql
rm /tmp/postgres.sql

In the above block of script, remember to replace the values given with your own values for:

  1. your normal database user’s username (in our case, railstest)
  2. your normal database user’s password (we’ll have to add this to our app in railstest/config/database.yml too)
  3. the name of the database you want to create for your app (in our case, railstest_production)

The apache script can be run as written.

The passenger script requires that one environment variable be set: the public hostname of your server (example: rails.example.com):

SERVERNAME=rails.example.com sh ./36-passenger.sh

After running the passenger script, let’s edit one of our Apache config files:

pico /etc/apache2/sites-available/default

Along with the addition and modification of a couple other options, we’re going to change the DocumentRoot to the directory where our app is going to live. Edit the VirtualHost block of the file to look like this:

<VirtualHost *>
    ServerAdmin youremail@yourdomain.com
    ServerName rails.example.com
    DocumentRoot /var/local/railstest/current/public
    RailsBaseURI /
    <Directory />
        Options FollowSymLinks
        AllowOverride None
    </Directory>
    <Directory /var/local/railstest/current/public>
        Options Indexes FollowSymLinks -MultiViews
        AllowOverride all
        Order allow,deny
        allow from all
    </Directory>
...

Save the file with Ctrl-O. Exit with Ctrl-X, and then run:

apache2ctl -k graceful

to restart Apache.

Finally, you can run the rails-apps script as written.

Post-Scripts

After we’ve finished running the scripts, we want to make it so that the server can connect to GitHub and access our app’s code repo during the deployment process.

Let’s generate an SSH keypair on the remote server, but as the “app” user instead of root, since that’s the one that we’ll be running the deployment process as:

sudo su app -c "ssh-keygen -t rsa"

As we did earlier with the keys generated on our Mac, we’ll paste the public key into our GitHub config:

  1. Run “cat /home/app/.ssh/id_rsa.pub” on the server and copy the result to the clipboard.
  2. On GitHub, go to your repo’s home page and click the “Admin” button.
  3. Under the “Deploy Keys” section, click “Add another deploy key”.
  4. Give the deploy key a title, like “Production App Server” or something.
  5. Paste the contents of the clipboard into the “Key” textarea.
  6. Click “Add Key”.

We’re done bootstrapping! Our Rackspace Cloud Server is now prepped and ready to run our app upon its arrival. Be sure to type “exit” to log out and close your SSH connection.

One More Thing

Before we call it quits for this episode, let’s go back to our Mac for a second and make sure we add the normal database user’s password to our config file. In Terminal, from our home directory, do the following:

cd workspace/railstest
pico config/database.yml

Go down to the empty “password:” line in the “production” section at the end of the file and add the password you specified above in the separate postgres.sql script (the one which created the normal user and the database). Type Ctrl-O to save the file.

Next Time

In our next episode, we’ll install Capistrano and deploy our app to this baby! See you then!

Categories: On the Ground Tags:

On the Rails: Set Up Git Repos Locally and On GitHub

December 31st, 2009 Comments off

GitHub

Let’s prepare GitHub for our code’s arrival.

  1. Go to github.com.
  2. If you don’t already have an account, click on “Pricing and Signup” at the top and get a free one.
  3. Log in to your account.
  4. Click “New Repository” in the upper right of the “Your Repositories” box.
  5. Give your repo the name “railstest”, along with an appropriate description and URL if you know where your project will live in production (i.e. its hostname).
  6. Click “Create Repository”.

Next, we need to generate an SSH keypair on our local machine, and add the public key to our GitHub account so our local machine can access the GitHub repo we just created. In a Terminal, issue the following command:

ssh-keygen -C "youremail@yourdomain.com" -t rsa

Accept the defaults, and don’t supply a passphrase unless you want to. Now, issue the following command to display the public key:

cat ~/.ssh/id_rsa.pub

Copy the contents of ~/.ssh/id_rsa.pub to the clipboard, and follow these steps:

  1. On GitHub, click “Account Settings” in the upper right of the page.
  2. Click “SSH Public Keys” in the second row of tabs.
  3. Click “Add another public key”.
  4. Give the key a title, like “Jeremy’s Key on MacBook”.
  5. Paste the contents of ~/.ssh/id_rsa.pub from the clipboard into the “Key” textarea.
  6. Click the “Add key” button.

Now, we’re ready to set things up locally.

Locally

With a few additions and modifications, we’ll follow the instructions you saw after creating the repo. In Terminal, from your home directory, issue the following commands in order:

git config --global user.name "Your Name"
git config --global user.email youremail@yourdomain.com
cd workspace/railstest
git init
git add .
git commit -m 'Initial import'
git remote add origin git@github.com:your_github_username/railstest.git
git push origin master

Now, let’s go back to our repo on GitHub and make sure our code made it there OK. That’s it!

Next time: We’ll create a Cloud Server on the Rackspace Cloud, set it up to host our app, and use Capistrano to deploy our code from GitHub to our server. See you then!

Categories: On the Ground Tags:

On the Rails: Install Apache 2 and Passenger Locally

December 31st, 2009 Comments off

For the sake of matching our production environment as closely as possible, let’s take a few minutes to install the Apache 2 web server and Phusion Passenger application layer locally. First, Apache 2:

sudo port install apache2

Next, Passenger:

sudo gem install passenger

Now, let’s build and install the Passenger extension module for Apache 2. This part was a little tricky for me, due to the fact that we’ve installed another Apache 2 via MacPorts. We want the Passenger module build process to find the MacPorts one, and not the one that came with Snow Leopard. I found and followed a couple other instructional blog posts on this, but I ended up doing something other than what they did to get it to work.

First, let’s add a line to the end of our ~/.profile file. (Type “pico ~/.profile” to edit it.)

export PATH="/opt/local/apache2/bin:$PATH"

Next, run the following command:

source ~/.profile

Now, we can build the module:

sudo passenger-install-apache2-module

After that builds and installs the module, let’s edit our Apache 2 config file. (Type “sudo pico /opt/local/apache2/conf/httpd.conf” to edit it.)

First, let’s type Ctrl-W and enter “80” to search for the string “80”. 80 is the default port Apache listens on. But that port is already bound to the Snow Leopard Apache. Let’s change the port our MacPorts Apache will listen on to 3000. Comment out this line (add a “#” character in front):

Listen 80

And add the following line right underneath it:

Listen 3000

Next, let’s add the following to the end of the file:

LoadModule passenger_module /opt/local/lib/ruby/gems/1.8/gems/passenger-2.2.8/ext/apache2/mod_passenger.so
PassengerRoot /opt/local/lib/ruby/gems/1.8/gems/passenger-2.2.8
PassengerRuby /opt/local/bin/ruby
PassengerDefaultUser your_username

<VirtualHost *:3000>
   ServerName localhost
   DocumentRoot /Users/your_username/workspace/railstest/public
   RailsBaseURI /
   RailsEnv development
   <Directory /Users/your_username/workspace/railstest/public>
      AllowOverride all
      Options Indexes FollowSymLinks -MultiViews
      Order allow,deny
      Allow from all
   </Directory>
</VirtualHost>

The Phusion Passenger users guide for Apache was helpful on this step. Be sure to replace “your_username” and “workspace” above with your username and the name of the directory containing your Rails projects.

Now, let’s start Apache:

sudo apachectl start

And, in a separate Terminal, start PostgreSQL if it’s not already running:

sudo su postgres -c '/opt/local/lib/postgresql84/bin/postgres -D /opt/local/var/db/postgresql84/railstest'

Browse to:

http://localhost:3000/books

and voila! Our CRUD app from last time is now running on Apache with Phusion Passenger instead of WEBrick!

Next up: Git and GitHub stuff.

Categories: On the Ground Tags:

On the Rails: Create a Simple CRUD App, Set Up a Database

December 29th, 2009 Comments off

Once you have everything installed, it’s actually pretty easy to create a basic application in Rails that can perform the four basic CRUD operations — Create, Read, Update, Delete — on a set of data.

Before we create the app, though, let’s briefly describe what our data is going to look like. Let’s say we want to store some basic information about all of the books we have in our possession. Most books have, among other things, a title, an author, a publishing company, a copyright year, and an ISBN number (a unique identifier). If our database table is going to be composed of books, then each record in that table is going to have each one of the above attributes, stored as strings of text.

Create CRUD App

So, let’s first create our initial, empty skeleton of a Rails app.  From your home directory, or whatever directory you want to store your Rails projects in, issue the following command:

rails railstest -d postgresql

This will create a folder named “railstest” and populate it with all of the wonderful goodies that Rails provides as a foundation for writing a web-based application that follows the Model-View-Controller architectural pattern. The “-d postgresql” part pre-configures the app to use the relational database system we have chosen, PostgreSQL. (If you wanted to use MySQL or something else, you could just as easily specify a command-line argument for whatever you’ve chosen instead. Type “rails –help” to see all the options.)

Now, we’re going to take our foundation and build a CRUD app on top of it. But we’re not going to type a single line of Ruby code. We’re going to make use of scaffolding. (Later, we’ll want to avoid scaffolding like the plague, and build our apps by actually writing our own Ruby code, but we’re trying to get up and running ASAP, so we’ll take the easy way for now.)

Issue the following commands from the directory containing your Rails project(s):

cd railstest
./script/generate scaffold book title:string author:string publisher:string copyright_year:string isbn:string

This will create some code files — models, views, and controllers — and a database migration, which describes how to set up our database schema. We’ll be invoking rake (a build program for Ruby) a little later to do just that.

Set Up Database

Before we do that, though, we should create a database cluster, launch the database server, create a new database user, and finally create the database. (You may have taken note of some instructions that appeared in your Terminal after you installed PostgreSQL. If you did, some of the commands in this step should look familiar.)

First, we need to create and initialize the data directory, or database cluster. Issue the following three commands in order:

sudo mkdir -p /opt/local/var/db/postgresql84/railstest
sudo chown postgres:postgres /opt/local/var/db/postgresql84/railstest
sudo su postgres -c '/opt/local/lib/postgresql84/bin/initdb -D /opt/local/var/db/postgresql84/railstest'

Next, launch the database server. Open a new Terminal tab or window, and then issue the following command:

sudo su postgres -c '/opt/local/lib/postgresql84/bin/postgres -D /opt/local/var/db/postgresql84/railstest'

Next, create a new database user (or role):

sudo su postgres -c '/opt/local/lib/postgresql84/bin/createuser -S -d -R railstest'

Finally, create the development database:

sudo su postgres -c '/opt/local/lib/postgresql84/bin/createdb -O railstest railstest_development'

Now, let’s run rake and create our schema:

rake db:migrate

Just Push Play

We’re almost there! Time to start up our web server and Ruby application layer. At the moment, all we have is WEBrick, which comes with Ruby. (Note: WEBrick is a quick and dirty web server solution that will run our simple app and get us to the end of this part of the tutorial, but it should not be used for anything more complicated, and certainly not in a production environment.) Run the following command in a new Terminal:

./script/server

Now, open your web browser and go to:

http://localhost:3000

You should see a Rails welcome screen. Next, go to:

http://localhost:3000/books

You should see our CRUD app! You should now be able to Create, Read (or Show), Update (or Edit) and Delete (or Destroy) books. The data is stored in our PostgreSQL database instance we created above.

Next Episode

Next time, we’ll put the code under version control locally using git, set up a remote repository on GitHub, and then push our code to that remote location. See you then!

Categories: On the Ground Tags:

Setting Up Rails and PostgreSQL on Snow Leopard

December 15th, 2009 Comments off

Hello again. This post will go over how to set up the Ruby language, the Ruby on Rails web framework, and the PostgreSQL relational database server on an Intel Mac running Mac OS X 10.6 (Snow Leopard).

Note: As the Perl folks say, there’s more than one way to do it. This is only one of many possible configurations and ways to set things up. I’m writing this post (and the next two or three) for my future self and anyone else who might want to get up and running with Rails this way.

In the next few posts, we will: create a simple database-driven CRUD web application in Rails; set up a source repository on GitHub and push our code there; create a server on the Rackspace Cloud, set it up for production use, and finally deploy our app from GitHub to that server.

This post is really an adaptation of a nice guide written by Jared McFarland. His setup includes MySQL rather than Postgres, and so my post will only modify and elaborate on his where appropriate.

Let’s begin.

MacPorts

The first thing we want to get is MacPorts, a system by which we can easily get the latest, most widely-used versions of Ruby, RubyGems (a Ruby software packaging system), Ruby on Rails, and PostgreSQL. (Snow Leopard does come with versions of Ruby, Gems and Rails, but things move fast, and they were already outdated when Snow Leopard was released.)

Go here and get the latest DMG disk image for Snow Leopard (1.8.1 at this writing). Install the PKG. (Note: MacPorts will put itself and everything else you install via MacPorts in the /opt folder on your primary hard disk, so you’ll know where everything is in case you want to get rid of it later.)

Ruby

[Update, 12/21/2009, 4:30 p.m. MST: Marc Chung tells me that a better way to install Ruby than that described in this section is via rvm (Ruby Version Manager), which allows you to have multiple Rubies installed and switch between them. More details forthcoming.]

In Terminal, do:

sudo port install ruby

and enter your user account password if/when prompted. This will install the latest Ruby 1.8, which at this writing is 1.8.7, patchlevel 174, dated June 12, 2009.

When it’s done, do:

which ruby

and you should see:

/opt/local/bin/ruby

Now, do:

ruby -v

and you should see:

ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin10]

RubyGems

sudo port install rb-rubygems

Latest RubyGems on MacPorts is 1.3.4. (1.3.5 is the actual latest and is coming to MacPorts Real Soon Now).

SVN and Git

sudo port install subversion
sudo port install git-core +svn

If you don’t have any existing Subversion repositories, you can ignore the subversion line and omit “+svn” from the git-core line. If you’re starting from scratch, you only need Git. Latest Git on MacPorts is 1.6.5.3 (actual latest 1.6.5.6).

PostgreSQL

sudo port install postgresql84-server

Latest PostgreSQL is 8.4.2, and it’s on MacPorts.

[Update, 12/24/2009, 12:30 am MST: Add the following line to the end of your ~/.profile file:

export PATH="/opt/local/lib/postgresql84/bin:$PATH"

Then issue the following command from the prompt:

source ~/.profile

The PostgreSQL binaries need to be in your PATH; if they aren't, the next step will fail.]

That’s all for the MacPorts stuff. Now for some Ruby Gems.

PostgreSQL Ruby Adapter

sudo gem install pg

Latest PostgreSQL Ruby Adapter gem (pg, a.k.a. ruby-pg) is 0.8.0.

Rails

sudo gem install rails

Latest Rails is 2.3.5. This includes eight gems — actionmailer, actionpack, activerecord, activeresource, activesupport, and rails (all 2.3.5); rack (1.0.1); and rake (0.8.7).

You now have all the software you need to start developing web applications using Ruby on Rails on your Snow Leopard Mac!

Be sure to tune in next time, for another exciting episode of Jeremy’s Wild and Crazy Adventures on (off?) the Rails, when we’ll discover how to create a basic CRUD app in Rails without writing a single line of Ruby code! (Before long, we’ll want to develop apps by actually writing some Ruby code, but more on that later.) We’ll also set up Git repositories in which to store and track our code revisions, both locally and on GitHub. See you then!

Categories: On the Ground Tags:

Did Apple “Push” Safari 4 Out to Millions of Users? Not Really.

June 13th, 2009 3 comments

Paul Thurrott, in a post featured prominently on Techmeme, wrote Friday that Apple is “making lemonade” in claiming that its new web browser, Safari 4, has been downloaded more than 11 million times.  Paul quotes approvingly from a piece by PC World’s Robert Strohmeyer which was cross-posted to Macworld:

As someone with three Macs at home, I couldn’t help but notice that Apple pushed Safari 4 out as an automatic update to all of its users this week. Yesterday, all three of the Macs in my household received the update, and we don’t even use Safari.

An informal poll of my friends and colleagues reveals a whole lot of the same. Got the update dialog, downloaded and installed it, don’t intend to use it.

What is at issue is the ridiculously thin claim that the latest Safari is a wild success on the basis that Apple basically pushed it out to everyone it possibly could, whether they wanted it or not.

(Emphasis Paul’s.)

What I take issue with is Robert’s equally misleading title (“Safari 4 download stat is pure hype”) and use of the phrase “pushed it out“.  I suppose it depends on how you define “push“, but to me, this reflects a lack of understanding of how Apple Software Update works.
Read more…

Categories: On the Ground Tags:

Cloud Computing != Grid Computing?

January 31st, 2009 Comments off

Following up to a previous post, I came across a July 2008 article by Thorsten von Eicken, CTO and founder of RightScale, which provides a front-end for managing Amazon Web Services (Amazon’s cloud computing offerings):

Grid computing has been used in environments where users make few but large allocation requests. … [O]nly a few of these allocations can be serviced at a time and others need to be scheduled for when resources are released. …

Cloud computing really is about lots of small allocation requests. … The allocations are real-time and in fact there is no provision for queueing allocations until someone else releases resources. …

It’s easy to say “ah, we’ll just run some cloud management software on a bunch of machines,” but it’s a completely different matter to uphold the premise of real-time resource availability. If you fail to provide resources when they are needed, the whole paradigm falls apart and users will start hoarding servers, allocating for peak usage instead of current usage…

The comments on that post are worth reading, as well.

An earlier post from Thorsten (republished later on Cloud Computing Journal) clearly and succinctly defines some terminology used to refer to different aspects of cloud computing:

Applications in the cloud: … Some company hosts an application in the internet… The service being sold (or offered in ad-sponsored form) is a complete end-user application. To me all this is SaaS, Software as a Service, looking to join the ‘cloud’ craze.

Platforms in the cloud: … where an application platform is offered to developers in the cloud. … The service being sold is the machinery that funnels requests to an application and makes the application tick.

Infrastructure in the cloud: … the most general offering that Amazon has pioneered and where RightScale offers its management platform. … virtually any application and any configuration that is fit for the internet can be mapped to this type of service.

Again, the comments are worth reading and link to other articles on the subject.

Categories: On the Ground Tags:

When Things Go Wrong…

January 31st, 2009 Comments off

Do you have a backup and recovery plan in place for your data, that you can turn to when the inevitable (catastrophic system failure and possible data loss) happens? Do you have a revision control system in place for your source code, that you can turn to when you break something in the functionality of your system?

Yesterday morning, a web-based bookmarking service, ma.gnolia, suffered a catastrophic failure. It will be at least a matter of days before the service returns, and users’ bookmarks may be lost, either partially or completely. Michael Calore writes at Wired’s Epicenter blog:

In light of today’s outage, many are questioning the reliability of web apps and web-based storage in general. Twitter in particular is full of users venting their suspicions.

“Cloud computing becomes fog when it goes down,” says Todd Spragins in a Twitter post.

Another common thread: People are talking about bailing on Ma.gnolia in favor of competitor Delicious.

More ammunition for the critics (warning: NSFW) of “the cloud”, or web-based software and data storage.
Read more…

Categories: On the Ground Tags: