Mar 23, 2009

Baby Step #1 with Rails, RSpec and Cucumber

There are lots of resources on the web to help get started with RSpec and Cucumber but there's still a learning curve that I think only experience will overcome. So I decided just to dive in. I don't claim what I've done here is right, or even a good idea, but it's where I started.

My initial goal is just to walk through the 'outside in' red/green/refactor process of writing a feature then examples without spending to much time worrying if I selected the right story or if it's implemented it correctly.

This initial cycle is mostly about learning what the tools can do for me.

The simplest story that I can imagine goes like this:

as a reader I want to browse the site so that I can read it's content.


So with that story in hand It's time to write my first feature
 #  ./features/reader_browses.feature
Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage
When I go to the homepage
Then I should see "Hello world!"


Next I run Cucumber, 'rake features', and get the following....

 Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage # features/reader_browses.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
undefined method `root_path' for # (NoMethodError)
/home/mgreenly/Projects/blog/features/support/paths.rb:6:in `/^I go to (.+)$/'
features/reader_browses.feature:7:in `When I go to the homepage'
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93

1 scenario
1 failed step
1 skipped step
rake aborted!


So the first error I get is because we don't have a default route, so lets fix it by adding the following line in "./config/routes.rb"

map.root :controller => 'posts'


Then I run Cucumber again, "rake features", and get the following....

 Feature: browsing
As a reader
I want to browse the site
So that I can read it's content

Scenario: visit the homepage # features/reader_browse.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
uninitialized constant PostsController (NameError)
(eval):2:in `/^I go to (.+)$/'
features/reader_browse.feature:7:in `When I go to the homepage'
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93

1 scenario
1 failed step
1 skipped step
rake aborted!


So now it's telling me that the default route points to a PostsController and that controller doesn't exist. So lets create both the controller and it's rspec framework with the built in generator.

./script/generate rspec_controller posts


Now I'll run the spec, "rake spec", to see what I get
 ..

Finished in 0.045209 seconds

2 examples, 0 failures


It passes of course because the controller doesn't do anything and the spec doesn't make any requirements on it.

So lets see what Cucumber says now, "rake features"
 Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage # features/reader_browses.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
No action responded to index. Actions: (ActionController::UnknownAction)
/usr/local/stow/ruby-1.8.7-p72/lib/ruby/1.8/benchmark.rb:308:in `realtime'
(eval):2:in `/^I go to (.+)$/'
features/reader_browses.feature:7:in `When I go to the homepage'
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93

1 scenario
1 failed step
1 skipped step
rake aborted!


There's still an error but it's changed. Now it's complaining because the there's no 'index' action on the Posts controller. So lets drop back to the rspec example for the Posts controller and create a requirement for the index action. Remember first I want it to fail, then pass.

# in ./spec/controllers/posts_controller_spec.rb
describe "GET 'index'" do
it "should be successful" do
get 'index'
response.should be_success
end
end


So now when I run "rake spec" I get....

.F.

1)
ActionController::UnknownAction in 'PostsController GET 'index' should be successful'
No action responded to index. Actions:
./spec/controllers/posts_controller_spec.rb:12:

Finished in 0.052123 seconds

3 examples, 1 failure
rake aborted!


Now that the spec is failing we can try to satisfy it. To do that I just create an empty file at "app/views/posts/index.erb" and then rerun "rake spec"
 ...

Finished in 0.049891 seconds

3 examples, 0 failures


So now that the spec passes I run Cucumber again, "rake features", and get ....
 Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage # features/reader_browses.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93
Could not parse document (RuntimeError)
features/reader_browses.feature:8:in `Then I should see "Hello world!"'

1 scenario
1 failed step
1 passed step
rake aborted!



It's still failing but now it's upset because it can't find the "Hello world!" text on the index page. This is easy to fix, I just need to add the text to "app/views/posts/index.erb". Now I run "rake features".....

Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage # features/reader_browses.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93

1 scenario
2 passed steps

It passes. Just to make sure I didn't screw anything up I run "rake spec" on more time.
...
Finished in 0.049713 seconds
3 examples, 0 failures


That makes one successfull pass through an outside-in red/green/refactor cycle using Cucumber and Rspec with Rails.

Start a BDD Rails Project

I was starting a new rails project today that I plan on developing with Cucumber and RSpec. I quickly realized that even though there are tons of tutorials out there none seemed to really describe the process of initializing a new project. So here it is in a nutshell.

$ rails blog
$ cd blog
$ ./script/generate rspec
$ ./script/generate cucumber
$ rake db:migrate

At this point you should be able to do a 'rake', 'rake spec' or 'rake features' command and not get any errors.

Rails Development On Ubuntu

There's no shortage of posts on the web describing how to setup Ubuntu for Rails development but I'm going to add one more anyway. I choose to write this because none of the posts I ran across were geared towards a current Rails, RSpec, Cucumber, Git setup.

I'm using Ubuntu 9.04 (JauntyJackalope) during this installation.

First install Ruby and it's associated packages.

sudo apt-get install ruby-full

Next install the native SQlite3 libraries and command line tools.
sudo apt-get install sqlite3 libsqlite3-dev

Next install some native xml processing libraries used by webrat.
sudo apt-get install libxml2-dev libxslt-dev

Next install Git
sudo apt-get install git-core

Next install the tools necessary to build native rubygem extensions.
sudo apt-get install ruby-dev build-essential

Next install RubyGems from source because the Ubuntu packages are usually not current. I also prefer to install RubyGems into my $HOME directory because mixing source packages in with distribution managed packages is generally a bad idea.
wget -c http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
tar xvzf rubygems-1.3.1
cd rubygems-1.3.1
ruby setup.rb --prefix=$HOME/.rubygems --no-format-executable

Once that's done you need to set some environment variables so that; Ruby can find the gems library, RubyGems can find the gem repository and executable gems will be in the $PATH. Add the following to the file $HOME/.bashrc
export RUBYLIB=$HOME/.rubygems/lib
export GEM_HOME=$HOME/.rubygems/gems
export PATH=$HOME/.rubygems/bin:$GEM_HOME/bin:$PATH

Then make sure the RubyGems library is up-to-date and install all the necessary gems.
gem update --system
gem install rake rails rspec-rails cucumber webrat sqlite3-ruby

If you want to double check your installation you can try the following...
ruby --version
gem --version
rails --version
spec --version
cucumber --version

RubyGems Documentation Index

In case there's anyone out there that isn't aware; running RubyGems server provides browseable documentation at http://localhost:8808/ for all the installed gems that have rdocs. This morning I decided to automate the launching of the gem server at startup by adding it to my bash profile.

# in $HOME/.profile
gem server --daemon

If you've previously installed some gems without documentation you can regenerate them this way.
gem rdoc --all

Feb 24, 2009

Twitter Search

For the first time ever I actually used Twitter for something productive today. I was having trouble with GMail this morning and was trying to decided how to verify if it was something on my end or Googles. So I decided to try http://search.twitter.com which gave me my answer as I watched tweets pile in announcing GMail was down.

Feb 22, 2009

Determine What Version Of Ubuntu Your're Running #2

A while back I wrote a post about determining which version of Ubuntu you're running here

They've since added the rather annoying -v option to lsb_release so now I prefer this

$ sudo apt-get install lsb-release
$ lsb_release -drci
# Distributor ID: Ubuntu
# Description: Ubuntu 8.10
# Release: 8.10
# Codename: intrepid

Nov 8, 2008

Upgrading to Ubuntu 8.10 (Intrepid Ibex)

In the past I've usually chose to run the early releases and install from scratch but, both because I'm busy and because my Laptop is so well supported out of the box these days I decided to do an upgrade this time around.

Here's a decent how-to if anyone is looking for directions.

The upgrade nearly went perfect. There was just one problem. There seems to be a bug in the Gnome upgrade that changes the way the bookmarks in the Places menu behave.

Here's the bug in Launchpad and here's the specific post that provided me with the fix.

Aug 10, 2008

Multiple Versions of Ruby On Ubuntu #2


This post is from a time before RVM or rbenv you should check out those instead..



I recently changed how I'm handling multiple simultaneous Ruby installations and I'd like to share.

What I needed was to make it convenient to switch between the system provided packages and specific, from source, installations. After some experiments I decided to use 'update-alternatives' to do it.

Here's a quick walk through...

First I removed all of my Ruby and RubyGem environment variables, they're not needed.

Then I installed Ubuntu's default Ruby packages via apt-get.
$ sudo apt-get install ruby irb ri rdoc libruby-extras rubygems ruby1.8-dev

Next I downloaded and installed alternate versions of Ruby from source. In this example I'm going to use two additional versions; the newest stable release and the newest development release.
$ cd /tmp
$ wget -c ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p71.tar.gz
$ tar -xvzf ruby-1.8.7-p71.tar.gz
$ cd ruby-1.8.7-p71
$ ./configure --prefix=/opt/ruby-1.8.7-p71
$ make
$ sudo make install
$ wget -c ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.0-3.tar.gz
$ tar -xvzf ruby-1.9.0-3.tar.gz
$ ./configure --prefix=/opt/ruby-ruby-1.9.0-3
$ make
$ sudo make install

At this point I have three versions of Ruby installed and each can be accessed through it's full path.
$ /usr/bin/ruby --version
# ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux]
$ /opt/ruby-1.8.7-p71/bin/ruby --version
# ruby 1.8.7 (2008-08-08 patchlevel 71) [i686-linux]
$ /opt/ruby-1.9.0-r18217/bin/ruby --version
# ruby 1.9.0 (2008-07-25 revision 18217) [i686-linux]

You'll also notice that the default installation is the one provided by Ubuntu.
$ ruby --version
# ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux]

Next we'll use 'update-alternatives' to make it a bit easier to switch between them. You could do this on the command line but it becomes a fairly long nasty command so I found it easier to write a quick shell script and run it. The script:
update-alternatives --install \
/usr/local/bin/ruby ruby /usr/bin/ruby 100 \
--slave /usr/local/bin/erb erb /usr/bin/erb \
--slave /usr/local/bin/gem gem /usr/bin/gem \
--slave /usr/local/bin/irb irb /usr/bin/irb \
--slave /usr/local/bin/rdoc rdoc /usr/bin/rdoc \
--slave /usr/local/bin/ri ri /usr/bin/ri \
--slave /usr/local/bin/testrb testrb /usr/bin/testrb

update-alternatives --install \
/usr/local/bin/ruby   ruby /opt/ruby-1.8.7-p71/bin/ruby 50 \
--slave /usr/local/bin/erb erb /opt/ruby-1.8.7-p71/bin/erb \
--slave /usr/local/bin/gem gem /opt/ruby-1.8.7-p71/bin/gem \
--slave /usr/local/bin/irb irb /opt/ruby-1.8.7-p71/bin/irb \
--slave /usr/local/bin/rdoc rdoc /opt/ruby-1.8.7-p71/bin/rdoc \
--slave /usr/local/bin/ri ri /opt/ruby-1.8.7-p71/bin/ri \
--slave /usr/local/bin/testrb testrb /opt/ruby-1.8.7-p71/bin/testrb

update-alternatives --install \
/usr/local/bin/ruby   ruby /opt/ruby-1.9.0-r18217/bin/ruby 25 \
--slave /usr/local/bin/erb erb /opt/ruby-1.9.0-r18217/bin/erb \
--slave /usr/local/bin/gem gem /opt/ruby-1.9.0-r18217/bin/gem \
--slave /usr/local/bin/irb irb /opt/ruby-1.9.0-r18217//bin/irb \
--slave /usr/local/bin/rdoc rdoc /opt/ruby-1.9.0-r18217/bin/rdoc \
--slave /usr/local/bin/ri ri /opt/ruby-1.9.0-r18217/bin/ri \
--slave /usr/local/bin/testrb testrb /opt/ruby-1.9.0-r18217/bin/testrb

What that does is create a group of applications under the generic name Ruby. In addition each application has several slave applications tied to it; erb, irb, etc... In defining each application we specify what symbolic link it will be accessed through and where the application is actually installed. In my case Ubuntu installed Ruby in /usr/bin and the source installed versions are in /opt. All of the installations will be accessed through the generic name Ruby and will have there symbolic links created in /usr/local/bin. I choose /usr/local/bin because it supercedes /usr/bin in the default path.

Before moving on make sure that 'update-alternatives' sees all of our Ruby installations:
$ update-alternatives --list ruby
# /opt/ruby-1.9.0-r18217/bin/ruby
# /opt/ruby-1.8.7-p71/bin/ruby
# /usr/bin/ruby

Now switching between them is as easy as running the 'update-alternatives' command and selecting the number of the installation you'd like to use. Example:
$ sudo update-alternatives --config ruby

It's important to keep in mind that each installation is separate. So for example if you install RubyGems while using /usr/bin/ruby it will not be available to /opt/ruby-1.9.0-r18217/bin/ruby, or /opt/ruby-1.8.7-p71/bin/ruby, etc....

While it's probably possible to use a shared repository for RubyGems across multiple installations I haven't tried it and instead have choosen to use multiple separate RubyGem installs, one for each Ruby installation.

Also RubyGem's bindir will most likely not be in your path. To get around this I created a short script called 'gemexec' in /usr/local/bin
#!/usr/bin/env ruby

require 'rubygems'

if ARGV.size > 1
exec "#{Gem.bindir}/#{ARGV.shift}",ARGV.join(" ")
else
exec "#{Gem.bindir}/#{ARGV.shift}"
end

This script uses the RubyGems installation of the currently selected Ruby to determine where the executable gem should be found, then runs it with any additional command line arguments provided. example:
$ gemexec rake --version
# rake, version 0.8.1

With all that in place the only thing to watch out for is other peoples scripts that hardcode the shebang line with something like "#!/usr/bin/ruby". What I do myself, and prefer in general, is to use "#!/usr/bin/env ruby".

The previous 'multiple versions of ruby on ubuntu'.

Setting CFLAGS #2

It turns out that with GCC 4.2.1 and later there is a new 'native' architecture flag for '-march' and '-mtune' that simplifies the setting of 'CFLAGS' greatly. Now instead of having to manually determine the exact architecture you can let GCC do it for you. If you trust it. Example:

CFLAGS="-march=native"
CXXFLAGS="${CFLAGS}"
export CFLAGS CXXFLAGS

previous post: 'Setting CFLAGS'

Jun 28, 2008

Determine What Version of Ubuntu You're Running?

The best way to determine the version of an Ubuntu (or any LSB compliant distribution) is to use the lsb-release command:

$ sudo apt-get install lsb-release
$ lsb_release -a
# Distributor ID: Ubuntu
# Description: Ubuntu 8.04
# Release: 8.04
# Codename: hardy