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.

Mar 31, 2009

Testing Rails Authlogic with Cucumber

In early rails projects I rolled my own authentication system, later I used restful-authentication. This time I'm going to try out AuthLogic which seems to be fairly popular, cleanly packaged and actively developed. I may walk through an AuthLogic installation in a future post but today I just want to make sure it can play nicely with cucumber.

First I wrote a fairly basic login scenario. You'll notice I stuck with the declarative style in writing this and I followed the advice provided in "The RSpec Book". I use direct model access to create the registered user in the 'Given' but then use simulated browser access to do the login and verify the actions success.

Scenario: successful login
Given I am the registered user John Doe
And I am on the login page
When I login with valid credentials
Then I should be on the account page
And I should see "Login successful!"


This left me with three steps to implement:

Given /^I am the registered user (.+)$/ do |login|
params = {
"login"=> login,
"password"=>"password",
"password_confirmation"=>"password"
}
@user = User.create(params)
end

When /^I login with valid credentials$/ do
fill_in('Login', :with => @user.login)
fill_in('Password', :with => "password")
click_button("Login")
end

Then /^I should be on ([^\"]*)$/ do |page_name|
response.request.path.should == path_to(page_name)
end

All fairly straight forward. I don't expect any problems using Cucumber with Authlogic.

Mar 27, 2009

Herding Tigers

I took the time last night to skim through most of the videos from the Mountain West Ruby Conference on Confreaks. Without question my favorite in the bunch was Daniel Philpott's - "Herding Tigers - Software Development and the Art of War".

If agile project management is something that interests you this video is worth watching.

Mar 26, 2009

The Facebook UI Changes

Lots of people have been talking about the UI changes on Facebook but I wasn't immediately sure of how I felt. I was more than willing to give the new look a chance. After all the old UI really sucked.

Well, I've decided. The new UI sucks even worse!

Oddly it took this change for me to understand what I was using Facebook for. Which has primarily been to watch status changes, photo uploads and exchange private messages.

The problem is that since the change everyone has started having much longer conversations on the "what's on you mind" post and unlike wall-to-wall conversations you can't opt-out of them.

I'm stuck reading them all! It sucks!

If I want to read through my updates to see things I'm interested in I have to wade through tons and tons of spam and it's not just conversation spam it's all the noise from the hundreds of absolutely retarded facebook applications.

The signal to noise ratio has dropped to the point where I've pretty much given up.

A Project Within A Project

So I've decided that I've learned what I need to from my Cucumber experiments[1,2] and now it's time to dive in for real but to do that I need to be sure I understand my objective and of course it's complicated like an onion. So I'm going to pop the why stack a few times.

My outer goal is to provide myself with a realistic Rails project that I can apply Agile, Behavior Driven Development techniques to. I'm writing the code so I can continue my education. I'm blogging about it so that I can generate some self promotion. I may benefit from the self promotion if I choose to pursue this career path again someday.

Of course it's necessary for the inner project to have legitimate driving factors as well, otherwise I can't really practice managing an agile process. So, here's a bit of background on the goals of the inner project...

The inner project is a DIY/HOWTO blog that's also an online store. The goal is to provide quality original content. So that it drives traffic to the site. So that related merchandise is exposed to reader traffic. So that stuff can be sold. So that I can make money.

So now with the stake holder's position explained it's time to start planning the project!

Pop The Why Stack

I think one of the most important idea's in lean/agile development is the attention devoted to only doing work where it provides value. One of the best expressions of this I've seen is on Cucumber's wiki page. Basically it says...

Before doing something pop the why stack recursively until you end up with one of the following business values; protect revenue, increase revenue or manage cost.

I think that's a powerful razor to cut things with!

Mar 25, 2009

Baby Step #2 with Rails, RSpec and Cucumber

In my previous post I took my first stab at Cucumber; first writing a failing Scenario, then getting it to pass, then writing a failing Spec and getting it to pass. The scenario in the previous post was overly simple, in fact I was able to complete it without writing any code at all. My goal this time is to continue my education with a slightly more realistic scenario.

One way to attack an application is in the same order it's used. In the case of a blog the author will need to add content before the site has any value, so that seems like a reasonable starting point.

# RAILS_ROOT/features/author_publish.feature
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post
Given I am on the post creation page
When I add a new post
Then I should see a page for the new post

The narrative for this feature was easy but deciding on how to approach the first scenario wasn't. In the end after browsing around the web and reading tons of material it was another post from Ben Maybe called "Imperative vs Declarative Scenarios in User Stories" that seemed to help me decide.

With that feature in place running cucumber:
./script/cucumber features/author_publish.feature

Produces the following:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
Can't find mapping from "the post creation page" to a path. (RuntimeError)
/home/mgreenly/Projects/blog/features/support/paths.rb:11:in `/^I am on (.+)$/'
features/author_publish.feature:7:in `Given I am on the post creation page'
When I add a new post # features/author_publish.feature:8
Then I should see a page for the new post # features/author_publish.feature:9

1 scenario
1 failed step
2 undefined steps

You can implement step definitions for missing steps with these snippets:

When /^I add a new post$/ do
pending
end

Then /^I should see a page for the new post$/ do
pending
end

The problem here is cucumber doesn't know what to map the phrase 'the post creation page' to. This can be fixed by adding the following code to the navigation helper.
# RAILS_ROOT/features/support/paths.rb
def path_to(page_name)
case page_name

when /the homepage/
root_path

when /the post creation page/
post_path("new")

else
raise "Can't find mapping from \"#{page_name}\" to a path."
end
end

Now we run cucumber again:
./script/cucumber features/author_publish.feature

And it produces the following:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
undefined method `post_path' for # (NoMethodError)
/home/mgreenly/Projects/blog/features/support/paths.rb:9:in `/^I am on (.+)$/'
features/author_publish.feature:7:in `Given I am on the post creation page'
When I add a new post # features/author_publish.feature:8
Then I should see a page for the new post # features/author_publish.feature:9

1 scenario
1 failed step
2 undefined steps

You can implement step definitions for missing steps with these snippets:

When /^I add a new post$/ do
pending
end

Then /^I should see a page for the new post$/ do
pending
end

Now it's complaining that it can't find a mapping for the Post resource. That makes sense since in my previous round I only created a controller. Instead of laboring through the process to create the Post model why not cheat and use the built in generator.
./script/generate rspec_scaffold -f Post title:string body:text

Obvious, going this route means I've broken my previously completed feature, so out of curiosity lets check it out.

Running cucumber on the original feature:
./script/cucumber features/reader_browse.feature

Produces 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
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93
expected the following element's content to include "Hello world!":
Posts: index
Listing posts
Title
Body
New post
(Spec::Expectations::ExpectationNotMetError)
features/reader_browse.feature:8:in `Then I should see "Hello world!"'

1 scenario
1 failed step
1 passed step


Broken as expected. The problem is that the index page no longer has the "Hello world!" text on it. No problem we'll fix that later, for now lets keep going on the new feature.

When I run cucumber now:
./script/cucumber features/author_publish.feature

I get:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
When I add a new post # features/author_publish.feature:8
Then I should see a page for the new post # features/author_publish.feature:9

1 scenario
2 undefined steps
1 passed step

You can implement step definitions for missing steps with these snippets:

When /^I add a new post$/ do
pending
end

Then /^I should see a page for the new post$/ do
pending
end

So now the it's obviously finding the 'the post creation page' but the remaining When/Then are still pending.

So now I add the following code:
# RAILS_ROOT/features/step_definitions/publish_steps
When /^I add a new post$/ do
fill_in "Title" , :with => "Some Title"
click_button "Create"
end


Then I run cucumber:
./script/cucumber features/author_publish.feature

And get the following:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
When I add a new post # features/step_definitions/posts_steps.rb:1
Then I should see a page for the new post # features/author_publish.feature:9

1 scenario
1 undefined step
2 passed steps

You can implement step definitions for missing steps with these snippets:

Then /^I should see a page for the new post$/ do
pending
end

Now I just need to satisfy the 'Then'
# RAILS_ROOT/features/step_definitions/publish_steps
Then /^I should see a page for the new post$/ do
response.should contain("Post was successfully created")
end


Then I run cucumber and get the following:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
When I add a new post # features/step_definitions/publish_steps.rb:1
Then I should see a page for the new post # features/step_definitions/publish_steps.rb:6

1 scenario
3 passed steps


Everything works, except the feature I broke, but I'll pick up there in the next post.