Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts

Jun 17, 2012

Just Maybe, Maybe - Pattern in Ruby

The original code I posted was flawed.  The gist below is modified and I think would work but it's not as clear as I'd like it to be.  I'll follow up on this soon.

I recently released a tiny Gem called JustMaybe.  This caused me to do more reading on the subject.  I ran across the If you gaze into nil nil gazes also into you post.  In the JustMaybe readme I sort of mention this but I wanted to express it more clearly here.

I don't really think using Maybe mixed in with the logic of your application is a good idea.  As an example I think the problem presented in that article could be solved much more clearly with this bit of code.  Here's a solution that will never return nil.



My suggestion is that if you find yourself wrestling with possible nil return values .  See if you can turn the operation on it's head.  Don't ask your object about it's associations.  Ask the association about your object.  I often find this will clean up the logic.

I did however just release JustMaybe so obviously I think it's useful in some way?  For me that's almost strictly in presentation.  When you are presenting information you often have to reach deep down into the hierarchy of objects to extract the necessary bits.  Interestingly, at presentation, is when I think you should be deciding what to display when information is missing.  To me that creates a perfect situation for using the Maybe pattern.


Feb 20, 2012

Extending Date Parsing In Ruby 1.9.3


I had previously made posts about parsing user supplied dates in Ruby



Apparently in Ruby 1.9.3 the Date library was re-implemented in C for performance and of course that broke my extension.  I updated it to support Ruby 1.9.3 below




This may not work on Ruby versions prior to 1.9.3 I didn't try it.

I also found the american_date gem which apparently existed before I had originally written my extension and is almost certainly a better choice if you don't need any custom formats.

Feb 6, 2012

Generic ".rvmrc" File For Ruby Projects


I'm really lazy and have started using the exact same ".rvmrc" file for all my Ruby projects....

rvm use @$(basename `pwd`) --create

I'm not sure but I think I first seen this exact format while reading this code https://github.com/dkubb/veritas

Jan 27, 2012

Generic "config/database.yml" for Postgres

I'm really lazy and always use the exact same "config/database.yml" file with my Rails projects....

Jan 6, 2012

More Parsing User Supplied Dates and Times in Rails


Rails does a pretty good job of making work with time zones feel transparent.  If you do everything it's way, including use ActionView::Helpers::DateHelper.  If you use a Javascript control that returns a text input field or if you allow the users to enter values directly or need to process uploaded content things get a bit more complicated.

If the string returned by the text input can be parsed by Date._parse your in luck.  Everything will still work as expected with no extra effort.  If on the other hand you want to do something as simple as display U.S. style dates, in the format "MM/DD/YYYY", you will have to manually parse the input.  Ruby's Date._parse understands 'YYYY/MM/DD' and 'DD/MM/YYYY' but not 'MM/DD/YYYY'.  In the case where you're processing the input from a Javascript control the most direct solution is to do something like this...



The example above does return a time value in the current timezone and will work as expected.  One big problem with strptime is that it isn't very flexible, the input string must match the provided format string exactly right down the the separator characters and spaces.  So this approach can work if your processing the output of a control but tends to fail if you need to process human input.

Another problem I have with the approach above is that it quickly becomes incredibly redundant if you're working with a large number of date/time fields in many different models.  To Dry that up a bit I decided to move my specialized processing into a modified version of Date._parse.  This means I don't have to litter my controllers with date time parsing code and I have a single entry point to test that all desired formats are supported.



Basically all this is doing is taking the input string and re-arranging the values into an order the original method will understand.  The biggest disadvantage to this problem is that it doesn't support per-locale input formats.  It guess I'll cross that bridge if I ever get to it.

Jan 4, 2012

Parsing User Supplied Dates and Times In Ruby


I'm in the middle of correcting some date/time handling in a Rails app. One of things that was wrong was the handling of user input date/time fields.  Obviously when users provide date/time values they're assuming we understand the information was provided from the perspective of their timezone.  Especially when we don't explicitly require them to provide timezone information.

The problem with this is that Ruby's DateTime.parse method always returns UTC based times.  You can see this clearly in the example below:
You can see the problem on the 6th line in the example above.  Even though the user input "2011/01/01T00:00" when that DateTime instance is converted to localtime the current timezone offset was subtracted.  In this case resulting in a time that's 6 hours earlier than desired.

The solution is straight forward of course. You just have to add the inverse of the users timezone offset to the value returned from DateTime.parse as demonstrated below.


On line 4 you can see it's correct even when converted from UTC to local time. 

As an alternative you could include the timezone information before parsing it but that's not always easy because you don't necessarily now the format of the user's input.


Of course all of this is generally pretty obvious but I shared just in case it can help some one less familiar with the issues.

Jan 3, 2012

Time Zones in Rails using Postgres


It appears that by default Rails creates it's timestamps in Postgres using timestamps without time zone information.  This means that if you're working directly with the database, using psql for example, and want to see local time representations you'll have to do something like this.

Specifically notice that I'm using the TIMEZONE function to re-cast the column to a timestamp with timezone.  Since the columns implied timezone is 'UTC' that's what's used.  Now that we have a timestamp with timezone we can convert that to local time using the "AT TIME ZONE" syntax.


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 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.

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 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.

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

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'.

Apr 5, 2008

NetBeans on Ubuntu

I'm usually a hardcore Vim fan but I've started to use NetBeans for my Ruby work. It's a fairly typical install but the one quirk is that $JAVAHOME has to be defined for the installer and Ubuntu's JDK package doesn't set it.

That's easily remedied though; download NetBeans, install the JDK and pass --javahome to the installer as a command line option.

#> netbeans-6.0.1-ml-linux.sh --java-home /usr/lib/jvm/java-6-sun

Mar 25, 2008

Before Filtering HTTP Method Types

Previously I wrote the post 'Method Not Allowed' which is about how I decided to handle unsupported HTTP request method types in my actions. Today I realized I should DRY up my code and put this logic into a before filter.

Feb 24, 2008

Marketing Open Source

Zed Shaw gives a talk about Mongrel where he touches heavily on how to market an Open Source project. It's worth watching if these things interest you at all, at the very least you'll learn what yak shaving is.

http://www.infoq.com/presentations/zed-shaw-mongrel-loc-economics