Archive

Archive for May, 2010

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: