Apr 26, 2009

Some Progress On My BDD Blog Project

I finally got around to doing some more work on my BDD Blog project over the weekend. Up to this point I had mostly been just experimenting (here, here and here), but this time I decided I was going to try and write and implement a real scenario.

My goal though is not so much to implement code. Instead I'm still trying to learn how to write features and specs. I want to get to the point where I'm comfortable enough with Cucumber, Webrat and RSpec that I'm confident I can express all the features for a release without wasting my time.

So lets start with the feature I'm going to try and implement. The feature it self is very straight forward. It walks through the process used by an author when creating a new post.

Scenario: save a draft post
Given I am logged in as John Doe
And I am on the post creation page
And I have filled in the title with "Get Rich Overnight"
And I have filled in the body with "lorem ipsum"
When I click the "Create" button
Then I should be on the post index page
And I should see "Post successfully created"
And I should see "1 to 1 of 1 posts"
And there should only be 1 post listed
And the first post's title should be "Get Rich Overnight"
And the first post's author should be "John Doe"
And the first post's status should be "draft"

I've already played around with AuthLogic and demonstrated it's use but there's a minor change here. I'm no longer testing authentication so I needed to reduce the login process to a single given statement.
Given I am logged in as John Doe

The code I used to do that is pretty straight foward.
Given /^I am logged in as (.+)$/ do |login|
Given "I am the registered user #{login}"
visit path_to("the login page")
fill_in('Login', :with => login)
fill_in('Password', :with => "password")
click_button("Login")
end

The only thing even remotely interesting here is that I'm calling one of my previously defined 'Given' statements from within this one.

The next few statements are very straight forward and were implemented entirely in cucumber with the existing scaffold for the Post resource.
And I am on the post creation page
And I have filled in the title with "Get Rich Overnight"
And I have filled in the body with "lorem ipsum"
When I click the "Create" button
Then I should be on the post index page
And I should see "Post successfully created"
The next line in this scenario:
And I should see "1 to 1 of 1 posts"
I now realize didn't belong in this scenario (I'll refactor that out later) but since I learned something while implementing it I'm going to share.

Specifically, what I learned was this statement has no real purpose. I wanted to make sure that only the new post was present but all this really does it check to see if the "1 to 1 of 1 posts" text is present. It doesn't provide any real validation of the number of posts on the page. In the future I'll move this out to a new feature that's specifically written to test the pagination summary.

This line was not originally in my feature:
And there should only be 1 post listed
I added it when I realized my mistake with the previous line. This statement directly tests the number of posts on the page by counting the table rows in the post index table. It's implementation is very straight forward:
Then /^there should only be (\d+) posts? listed$/ do |count|
response.should have_tag("tr" , :count =>(count.to_i + 1))
end

The next three lines:
And the first post's title should be "Get Rich Overnight"
And the first post's author should be "John Doe"
And the first post's status should be "draft"

These lines were all very similar. They are simply looking to make sure that the contents of specific columns in the post index table have the correct information. This statement is implemented with the following code:
Then /^the (.+) post's (.+) should be "([^\"]*)"$/ do |index, column, text|
response.should have_selector("table>tr:nth-child(#{position_to(index) + 1})") do |tr|
tr.should have_selector("td[class=
'#{column}']") do |td|
td.should contain(text)
end
end
end
There's not much special there but it was my first attempt at really trying to use the CSS selector notation. Maybe after I'm more comfortable with it I'll devote a post to just that but for now I'll just explain this method.

In a nutshell it simply looks to make sure that nth row in the post index table has a td tag with a class matching the column name and that it's content matches the text passed in.

You may also notice I have a poorly named method called position_to in this method that simply turns the english words; first, second, third, etc... into the appropriate fixnum. Hopefully down the road I can find a library that does this but if not maybe I'll expand my approach into a full fledged gem.

This pretty much covered what I did with Cucumber and Webrat to fully implement this scenario but I did dip down into some view, controller and model specs to get the job done. I'll save that for another post.

Can't Use Ruby 1.9.1 Just Yet

I had wanted to switch to Ruby 1.9 for my current project. I'm using a fairly minimal number of dependencies and didn't expect any problems. Unfortunately for what ever reason RSpec fails silently under Ruby1.9.1 but works properly on Ruby1.8.7 (at least on my machine). Since I don't have the time to track it down right now it looks like I'm reverting back to Ruby1.8.7 for the time being.

Hopefully I'll have time to dig into it later in the week.

Apr 24, 2009

The 'sudo' Command's Environment

I'm sure most people are aware the 'sudo' command sterilizes the user's environment variables before executing the command passed to it. The most common issue I've run into with this is $PATH not being preserved. The usual scenario is that I have an application installed into /opt and I've appended it's location to $PATH, which works just fine until you try to execute the application with 'sudo' and it fails because the command is no longer in the current path.

The fix to this is really straight forward and I'm surprised I hadn't thought of it earlier. Simply add the following to your $HOME/.bashrc file.

alias sudo="sudo env PATH=$PATH"
What this does is use 'sudo' to run 'env' which is passed the provided command but sets the target environment's PATH to match the current users before executing it.

Don't forget that you can always run the unaliased version of a command simply by prefixing it with a backslash.

Apr 12, 2009

Multiple Versions of Ruby on Ubuntu #3



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

I decided it's time to switch to Ruby 1.9 but I still have a couple of Ruby 1.8 projects I need to maintain. That means I need a simple technique for switching back and forth between multiple versions of Ruby. I've experimented with different approaches in the past but I'm not completely satisfied with any of them.

This time I've decided to use a shell script to update a symbolic link that points to the active version. It's extremely fast and simple.

I started by purging my system of any apt installed Ruby packages.
sudo apt-get remove --purge ruby1.8 ruby1.9

Then I installed Ruby 1.8 from source.
sudo apt-get build-dep ruby1.8
wget -c ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz
tar -xvzf ruby-1.8.7-p72.tar.gz
cd ruby-1.8.7-p72
./configure --prefix=/opt/ruby-1.8.7-p72 --enable-pthread --enable-shared --enable-openssl --enable-readline --enable-zlib
make
sudo make install
wget -c http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
tar -xvzf rubygems-1.3.1.tgz
cd rubygems-1.3.1
sudo /opt/ruby-1.8.7-p72/bin/ruby setup.rb

Then I installed Ruby 1.9 from source.
sudo apt-get build-dep ruby1.9
wget -c ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p0.tar.gz
tar -xvzf ruby-1.9.1-p0.tar.gz
cd ruby-1.9.1-p0/
./configure --prefix=/opt/ruby-1.9.1-p0 --enable-pthread --enable-shared
make
sudo make install

Then I add the following line to the bottom of $HOME/.bashrc
export PATH=/opt/ruby/bin:$PATH

Then I saved the following script in /usr/local/bin/select_ruby
#!/bin/sh

while : # Loop forever
do
cat << !

current = $(/opt/ruby/bin/ruby --version)

Select Option

1. ruby-1.8.6-p368
2. ruby-1.8.7-p72
3. ruby-1.8.7-p160
4. ruby-1.9.1-p0
5. exit

!

echo -n " Your choice? : "
read choice

case $choice in
1) rm -rf /opt/ruby; ln -s /opt/ruby-1.8.6-p368 /opt/ruby;;
2) rm -rf /opt/ruby; ln -s /opt/ruby-1.8.7-p72 /opt/ruby;;
3) rm -rf /opt/ruby; ln -s /opt/ruby-1.8.7-p160 /opt/ruby;;
4) rm -rf /opt/ruby; ln -s /opt/ruby-1.9.1-p0 /opt/ruby;;
5) exit;;
*) echo "\"$choice\" is not valid "; sleep 2 ;;
esac
exit
done

Now to switch I just run the script and select the version I want.
sudo select_ruby

There's a couple of things to be aware of using this approach. Each ruby installation has it's own separate instance of rubygems so gems will have to be installed multiple times. Also since sudo doesn't preserve the users $PATH you will need to use the full path with the gem command; example:
sudo /opt/ruby/bin/gem install rails
Or this fix.

Apr 5, 2009

As The Prouduct Owner What's My Goal?

I've been away from rails for a bit and to catch up I've decided to write an application to re-familiarize myself with the frame work and at the same time try and apply what I think I've learned about behavior driven development. There's an earlier post with more details.

Today, in this post, I'm going to be the product owner and attempt to define my goals for this project.

I want to create a blog with built in shopping cart functionality. The intention is that the original content generated for the blog will generate traffic to the site that can be converted into sales. I realize that if this was my only goal it would be smarter to use an existing blog framework that has an available shopping cart plugin, but it's not my only goal.

I want to break development into two major parts. The first phase will be to develop the blog and the second the shopping cart functionality. I'm going this route because it will allow the blog to start building traffic while the shopping cart is being developed.

This means I need to define what I want in a blog....

  • It should support a write to draft, edit, preview then publish work flow. When drafts are previewed they should be seen exactly as they will appear on the finished page. Nothing annoys me more about blogger than the fact that it's preview does not use the blog's styles sheets.
  • It should provide a sane default permalink based on the title but it should also allow the permalink to be overridden.
  • It should allow additional alias permalinks. This is specifically to make importing the content of other blogs with different permalink formats easier.
  • The markup should be a subset of html. I don't need a WYSIWYG editor or a markdown syntax.
  • It should handle tags for blog articles and be able to display a tag cloud on the side bar.
  • It should support interactive editing of javascript widgets for the side bar.
  • It should handle article grouping to create a series of articles. When an article is made part of a series it should automatically include links to the previous and next articles in the series.
  • It obviously needs to generate rss/atom feeds.
  • It should support search but it can be an external service (google site search) if it's incorporated really well.
  • It should support online editing of the site wide css. It shouldn't be necessary to change sources code, commit and deploy it just for a CSS tweak.
  • It should support comments from authenticated users (OpenID and Facebook authentication) and anonymous comments after review.
  • It should be able to import a blog from Blogger.
  • It should support syntax highlighting for ruby, xml, yaml, css, html and json code.
  • It should be able to compile a daily digest of Twitter comments.
  • It shoudl be able to compile a daily digest of Delicious links.
That spells out pretty much what I'm after.

Apr 4, 2009

Python's DVCS Comparison

I ran across this comparison of distributed version control systems. It also appears that Python has selected Mercurial after doing this comparison. I don't really have a comment on that but I do find it interesting how different communities gravitated in different directions. With Ruby users firmly entrenched in the world of Git and now Python going with Mercurial.

Still while reading through it I found at least one thing I wanted to share. Towards the end of the comparison there's a section that evaluates how long it takes to update a stale repository. In that section there's a comment that indicates that git was not able to checkout a repository at a specific commit but I'm not sure how that effected that test?

To perform this test I would have used this...

# fetch a copy of the repository
git clone [url]
# roll everything back 700 commits
git reset --hard HEAD~700
# pull the head from remote master
git pull

I think I misunderstood what they were trying to test. It looks like they wanted to test the performance of the fetch, merge, and commit and what I described above doesn't re-fetch the 700 commits.