Saturday, September 30, 2006

Rails Model View Controller + Presenter?

Ruby on Rails directs you to use Model View Controller by convention. This often results in a one to many relationship between Controllers and Views. A common controller could contain the following code.
class StandardsController < ApplicationController

def list
@standards = Standard.find(:all)
end

def results
standards = Standard.find(:all)
@total_standards = standards.size
@standards_for_hr = standards.select { |standard| standard.department == 'hr }
@standard_categories = standards.collect { |standard| standard.category }.uniq
...
end

end
This controller implementation works fine; however, the results method can become a bear to test. To solve this issue my team began inserting another layer: a Presenter. The main responsibility of a Presenter is to expose data for the view to consume. After introducing the presenter the controller becomes much slimmer.
class StandardsController < ApplicationController

def list
@standards = Standard.find(:all)
end

def results
@presenter = Standard::ResultsPresenter.new()
end

end
The Standard::ResultsPresenter class handles aggregating all the required data.
class Standard::ResultsPresenter

def total_standards
standards.size
end

def standards_for_hr
standards.select { |standard| standard.department == 'hr }
end

def standard_categories
standards.collect { |standard| standard.category }.uniq
end

def standards
Standard.find(:all)
end

end
After introducing this abstraction the Presenter can be tested in isolation.
class Standard::ResultsPresenterTest < Test::Unit::TestCase

def test_total_standards
Standard.expects(:find).with(:all).returns([1,2,3])
assert_equal 3, Standard::ResultsPresenter.total_standards
end

def test_standards_for_hr
standard_stubs = [stub(:department=>'hr'), stub(:department=>'other')]
Standard.expects(:find).with(:all).returns(standard_stubs)
assert_equal 1, Standard::ResultsPresenter.standards_for_hr.size
end

def test_standard_categories
standard_stubs = [stub(:catagory=>'Ruby'), stub(:catagor=>'Ruby')]
Standard.expects(:find).with(:all).returns(standard_stubs)
assert_equal ['Ruby'], Standard::ResultsPresenter.standard_categories
end

end
We like this style because it's much cleaner than the previously required code that lived in the controller tests. However, there is overhead involved in generating this additional layer. Because of the overhead we generally don't add a presenter until we notice that testing has become painful in the controller test file.

Friday, September 29, 2006

Remove Subversion from a tree

The following code will remove all the .svn folders from a tree (starting at the current file's directory).
Find.find(File.dirname(__FILE__)) { |path| `rm -rf #{path}` if path =~ /\.svn$/}
I've needed this enough times that I thought it might save someone else some time.

When did I use this? Every time I start a new project I check it into a local subversion repository. After working on the code for a bit, if I decide that it's a project worth distributing I register it somewhere else (such as RubyForge). If my project is accepted, I'll transfer the code to the projects repository. The easiest way, I've found, to move the code is to remove the local .svn directories and check out version 0 of the new repository into the project's root. At this point, I can svn add everything and check in.

I'm sure there's a better way, but this is simple and it works.

Tuesday, September 26, 2006

Test::Unit test creation

Creating a unit test in Ruby is very similar to Java: create a method that begins with test_. In a recent discussion with some colleagues, Zak Tamsen and Muness Alrubaie, Zak described his distaste for this common convention. He continued by pointing out that using .NET Attributes was a superior approach for identifying tests.

This began a discussion on what a superior approach to writing tests could be when using Ruby. We implemented several examples before deciding on our preference.
test
def add_returns_the_sum_of_two_numbers
assert_equal 4, add(2, 2)
end

test 'add returns the sum of two numbers'
assert_equal 4 do
add(2,2)
end
Each implementation contained limitations. In the end we chose to go with a very simple implementation that is similar to how RSpec defines specifications.
test 'add returns the sum of two numbers' do
assert_equal 4, add(2, 2)
end
To accommodate this syntax we added the test class method to Test::Unit::TestCase.
class << Test::Unit::TestCase
def test(name, &block)
test_name = :"test_#{name.gsub(' ','_')}"
raise ArgumentError, "#{test_name} is already defined" if self.instance_methods.include? test_name.to_s
define_method test_name, &block
end
end
An additional benefit we were able to include in our change was the ability to raise an error when a test was defined with a duplicate name (or description in our case).

However, moving to this new style of test definition can cause issues. The first noticeable issue was that TextMate would no longer run individual tests. We quickly fixed this issue by looking at the code in the Ruby Bundle. The fix was to change the regex that selects the method you are testing. The code below shows how you can capture either the previous call to the test class method or the previously defined test method.
File.open(ENV['TM_FILEPATH']) do |f|
f.read.split("\n")[0...n].reverse.each do |line|
if line =~ /^\s*test\s+"([_a-z][_a-z0-9 ]*[\?!]?)"\s+do\s*$/i
print "test_#{$1.gsub(' ', '_')}"
break
elsif line =~ /^\s*def ([_a-z][_a-z0-9]*[\?!]?)/i
print $1
break
end
end
end
We also created a TextMate snippet in the Ruby Bundle to help us create tests in our new style. The following code is the snippet for easily creating tests.
test "${1:test name}" do
$0
end
After these quick changes we are quite happy working with our new format for creating tests.

Thursday, September 21, 2006

Ruby Stub Variations: Stubba

Ruby Stub Variations: Introduction
Ruby Stub Variations: OpenStruct
Ruby Stub Variations: TestStub
Ruby Stub Variations: Struct
Ruby Stub Variations: Stubba

In my last entry I described how, using Struct, you can create stubs without creating your own stub implementations. Struct also provides you the power of creating and keeping reference to a class. But, on many occasions you don't need the extra capability. On these occasions the unnecessary flexibility requires you to enter additional code. There is another alternative that allows you to initialize a class similarly to OpenStruct: Stubba.

Stubba is a stubbing framework that provides great flexibility. The example test can be implemented using Stubba with the following code.
def test_values_are_appened_to_insert_statement
statement = Insert.into[:table_name].values do
stub(:to_sql=>'select column1, column2 from table2')
end
assert_equal "insert into table_name select column1, column2 from table2", statement.to_sql
end
Stubba provides the same simplicity of OpenStruct and addresses the limitations. Stubba correctly responds to predefined methods and even returns as expected from respond_to?. However, the major benefit to using Stubba is the ability to temporarily change behavior of existing objects. For example, if I chose to use the Select class, but wanted to stub out the to_sql method I would use the following code.
def test_values_are_appened_to_insert_statement
Select.any_instance.stubs(:to_sql).returns('select column1, column2 from table2')
statement = Insert.into[:table_name].values do
Select[:column1, :column2].from[:table2]
end
assert_equal "insert into table_name select column1, column2 from table2", statement.to_sql
end
Stubba is a great alternative to the previously mentioned stub alternatives. In the end, which stub you choose should be based on your needs of your project.

Tuesday, September 19, 2006

Ruby Form Template Method Using Extend

In Martin Fowler's book, Refactoring, he describes how you can form a template method to eliminate duplicate behavior. I wont get into the motivations for doing such a thing; for that, I suggest buying the book. However, I would like to use the example to show how I would implement it using Ruby.

The prefactored code in Ruby could look like this:
class Customer
def statement
result = "Rental Record for #{name}\n"
_rentals.each do |rental|
result += "\t#{rental.movie.title}\t#{rental.charge}\n"
end
result += "Amount owed is #{total_charge}\n"
result += "You earned #{total_frequent_renter_points} frequent renter points"
end

def html_statement
result = "<h1>Rentals for <em>#{name}</em></h1><p>\n"
_rentals.each do |rental|
result += "#{rental.movie.title}: #{rental.charge}<br>\n"
end
result += "<p>You owe <em>#{total_charge}</em></p>\n"
result += "On this rental you earned <em>#{total_frequent_renter_points}</em> frequent renter points<p>"
end
end
The first refactoring step is to create a Statement base class and two derived classes that inherit from Statement. However, we will need a Statement class and two modules that contain the methods we require.
class Statement
end

module TextStatement
end

module HtmlStatement
end
The next step is to alter Customer to use our new Statement class.
class Customer
def statement
Statement.new.extend(TextStatement).value(self)
end

def html_statement
Statement.new.extend(HtmlStatement).value(self)
end
end

module TextStatement
def value(customer)
result = "Rental Record for #{customer.name}\n"
customer.rentals.each do |rental|
result += "\t#{rental.movie.title}\t#{rental.charge}\n"
end
result += "Amount owed is #{customer.total_charge}\n"
result += "You earned #{customer.total_frequent_renter_points} frequent renter points"
end
end

module HtmlStatment
def value(customer)
result = "<h1>Rentals for <em>#{customer.name}</em></h1><p>\n"
customer.rentals.each do |rental|
result += "#{rental.movie.title}: #{rental.charge}<br>\n"
end
result += "<p>You owe <em>#{customer.total_charge}</em></p>\n"
result += "On this rental you earned <em>#{customer.total_frequent_renter_points}</em> frequent renter points<p>"
end
end
Next, Martin begins to move the differing behavior to separate methods.
module TextStatement
def value(customer)
result = header_string(customer)
customer.rentals.each do |rental|
result += each_rental_string(rental)
end
result += footer_string(customer)
end

def header_string(customer)
"Rental Record for #{customer.name}\n"
end

def each_rental_string(rental)
"\t#{rental.movie.title}\t#{rental.charge}\n"
end

def footer_string(customer)
"Amount owed is #{customer.total_charge}\n" +
"You earned #{customer.total_frequent_renter_points} frequent renter points"
end
end

module HtmlStatement
def value(customer)
result = header_string(customer)
customer.rentals.each do |rental|
result += each_rental_string(rental)
end
result += footer_string(customer)
end

def header_string(customer)
"<h1>Rentals for <em>#{customer.name}</em></h1><p>\n"
end

def each_rental_string(rental)
"#{rental.movie.title}: #{rental.charge}<br>\n"
end

def footer_string(customer)
"<p>You owe <em>#{customer.total_charge}</em></p>\n" +
"On this rental you earned <em>#{customer.total_frequent_renter_points}</em> frequent renter points<p>"
end
end
The final (hopefully obvious) step is to pull up the value method.
class Statement
def value(customer)
result = header_string(customer)
customer.rentals.each do |rental|
result += each_rental_string(rental)
end
result += footer_string(customer)
end
end

module TextStatement
def header_string(customer)
"Rental Record for #{customer.name}\n"
end

def each_rental_string(rental)
"\t#{rental.movie.title}\t#{rental.charge}\n"
end

def footer_string(customer)
"Amount owed is #{customer.total_charge}\n" +
"You earned #{customer.total_frequent_renter_points} frequent renter points"
end
end

module HtmlStatement
def header_string(customer)
"<h1>Rentals for <em>#{customer.name}</em></h1><p>\n"
end

def each_rental_string(rental)
"#{rental.movie.title}: #{rental.charge}<br>\n"
end

def footer_string(customer)
"<p>You owe <em>#{customer.total_charge}</em></p>\n" +
"On this rental you earned <em>#{customer.total_frequent_renter_points}</em> frequent renter points<p>"
end
end
At this point, forming a template method by using extend should be clear. But why use extend instead of inheritance? The answer is you would use extend if the modules you were creating could be used to extend various classes.

For example, let's imagine that the next requirement of our application is to display the above information for only the previous month. The current statement class gives a list of each rental associated with a customer for all months. To satisfy our new requirement we could create a MonthlyStatement class similar to the code below.
MonthlyStatement
def value(customer)
result = header_string(customer)
rentals = customer.rentals.collect { |rental| rental.date > DateTime.now - 30 }
rentals.each do |rental|
result += each_rental_string(rental)
end
end
end
The advantage to the module approach and mixins is now clear: if we had chosen inheritance we would not need to create a TextMonthlyStatement class and a HtmlMonthlyStatement class. However, because we chose to use modules instead of inheritance, we can simply mixin their behavior and achieve reuse without additional classes.
class Customer
def statement
Statement.new.extend(TextStatement).value(self)
end

def html_statement
Statement.new.extend(HtmlStatement).value(self)
end

def monthly_statement
MonthlyStatement.new.extend(TextStatement).value(self)
end

def monthly_html_statement
MonthlyStatement.new.extend(HtmlStatement).value(self)
end
end

Monday, September 18, 2006

A Ruby DSL for generating SQL

On my past 2 projects I've needed the ability to create ad-hoc SQL statements. These statements, following creation, are executed by using ActiveRecord::Base.connection.execute. On the first project we largely used strings to represent our required SQL. However, on our second project we decided to create a DSL that allowed us to stay in our comfortable Ruby world. That DSL is a bit more heavy-weight; however, I took the experience I gained from that project and put together a light-weight version that I've released on RubyForge.org.

The idea of the DSL was to keep as similar to SQL as possible. Below are examples of both the actual SQL and Ruby version that allowed us to generate the SQL.
Select column1 from table1 
where column2 = 12
and column 3 = 13

Select[:column1].from[:table1].where do
equal :column2, 12
equal :column3, 13
end

insert into table1 (column1, column2, column3) values (10, 'book', 'start')

Insert.into[:table1][:column1, :column2, :column3].values(10, 'book', 'start')

update table1 set column1 = 12, column2 = 'book'
where column1 = 10

Update[:table1].set[:column1=>12, :column2=>'book'].where do
equal :column1, 10
end

delete from table1 where column1 = 12

Delete.from[:table1].where do
equal :column1 = 12
end
The DSL does not contain any field validation or verify that the columns exist in the specified tables or any other magic. Currently, it's sole purpose is to allow you to easily write SQL with Ruby code.

If you are lucky you may never have any need for such a library; however, if you use find_by_sql on ActiveRecord::Base or select_values, select_value, or select_one on ActiveRecord::Base.connection you may want to give the DSL a look.

To install SQL DSL you can:
gem install sqldsl
Once installed all you need to do is require 'sqldsl' and all should be well.

For more information check out the SQL DSL documentation on RubyForge.org

Thursday, September 14, 2006

Ruby Stub Variations: Struct

Ruby Stub Variations: Introduction
Ruby Stub Variations: OpenStruct
Ruby Stub Variations: TestStub
Ruby Stub Variations: Struct
Ruby Stub Variations: Stubba

In my last entry I described how TestStub provided a concise syntax for defining stubs on the fly. However, I always consider it a win when I can remove code from the codebase without removing functionality.

Ruby provides a Struct class that allows you to define classes on the fly. From the Ruby documentation:
A Struct is a convenient way to bundle a number of attributes together, using accessor methods, without having to write an explicit class.
Struct also provides a one line solution to the example test.
def test_values_are_appened_to_insert_statement
statement = Insert.into[:table_name].values do
Struct.new(:to_sql).new('select column1, column2 from table2')
end
assert_equal "insert into table_name select column1, column2 from table2", statement.to_sql
end
The syntax of Struct doesn't seem as natural since the proliferation of using hashes as parameters, but Struct does provide the flexability of creating a new class and initializing at a later time. Struct works well and does not suffer from any of the limitations of the other examples.

Wednesday, September 13, 2006

Ruby Stub Variations: TestStub

Ruby Stub Variations: Introduction
Ruby Stub Variations: OpenStruct
Ruby Stub Variations: TestStub
Ruby Stub Variations: Struct
Ruby Stub Variations: Stubba

As I mentioned in my last entry, OpenStruct provides the desired simplicity when creating a stub. However, OpenStruct's inability to stub previously defined methods made it painful to work with when stubbing ActiveRecord::Base subclass instances. To resolve this issue my team borrowed some of the code from OpenStruct and wrote their own TestStub class. I previously wrote about this decision so I wont go too deep into detail; however, the general idea is that defining methods, instead of depending on a hash, resolved this issue. The implementation we ended up using was a bit simpler than the previous example though.
class TestStub
def initialize(*should_respond_to)
@table = {}
should_respond_to.each do |item|
create_readers(item) and next if item.kind_of? Hash
create_writer(item) and next if item.kind_of? Symbol
end
end

def create_readers(item)
item.each_pair do |key, val|
self.class.send(:define_method, key.to_sym) { val }
end
end

def create_writer(item)
self.class.send(:define_method, :"#{item}=") { }
end
end
TestStub provided us with the same ease of use that OpenStruct provided, thus the solution to example test is basically the same.
def test_values_are_appened_to_insert_statement
statement = Insert.into[:table_name].values do
TestStub.new(:to_sql=>'select column1, column2 from table2')
end
assert_equal "insert into table_name select column1, column2 from table2", statement.to_sql
end
TestStub never gave us any trouble; however, it was one more piece of code (and tests) to maintain.

Tuesday, September 12, 2006

Ruby Stub Variations: OpenStruct

Ruby Stub Variations: Introduction
Ruby Stub Variations: OpenStruct
Ruby Stub Variations: TestStub
Ruby Stub Variations: Struct
Ruby Stub Variations: Stubba


Shortly after I begin to program full time using Ruby I was exposed to OpenStruct. I was quite pleased with the simplicty OpenStruct offered. OpenStruct allows you to create objects with their attributes initialized. For example:
require 'ostruct'
record = OpenStruct.new(:name=>'jay')
puts record.name # -> jay
OpenStruct provided value by allowing me to easily create stubs in one line of code. The code below represents a more robust version of the example test. OpenStruct helps reduce the fragility of the test by removing the dependency on the Select class.
def test_values_are_appened_to_insert_statement
statement = Insert.into[:table_name].values do
OpenStruct.new(:to_sql=>'select column1, column2 from table2')
end
assert_equal "insert into table_name select column1, column2 from table2", statement.to_sql
end
Despite the value of OpenStruct, it also has some limitations. The first issue we encountered was that it did not respond as expected when the OpenStruct instance was frozen.

Another issue with OpenStruct is that it does not respond as expected if you specify a value for an already defined method. My team noticed this behavior while using an OpenStruct instance in place of a ActiveRecord::Base subclass instance. The test required the OpenStruct instance to respond to id; however, the number that was returned did not match the value passed in the constructor. The following failing test should demonstrate the described issue.
require 'test/unit'
require 'ostruct'

class OpenStructTest < Test::Unit::TestCase
def test_id
assert_equal 2, OpenStruct.new(:id=>2).id
end
end
This issue stems from the current implementation of OpenStruct. OpenStruct stores it's attribute values in a Hash. When you attempt to access an attribute the call is actually delegated to method_missing. OpenStruct's implementation of method_missing returns the value from the Hash if it finds a matching key, otherwise nil. Unfortunately, method_missing will never be called if a method, such as id, is previously defined.

Monday, September 11, 2006

Ruby Stub Variations

The majority of my Unit Tests focus on testing individual classes one method or behavior at a time. To facilitate this type of testing the class under test often collaborates with stub classes. Ruby offers several choices for stubbing.

To demonstrate usage for the various types of stubs I'll use a test from our test suite and show the differing implementations. The example comes from our tests that ensure our SQL DSL behaves as expected. This test verifies that to_sql is called on the object that is in the block being passed to the values method and the return value of to_sql is appended to the sql being generated by the Insert instance.
def test_values_are_appened_to_insert_statement
statement = Insert.into[:table_name].values do
Select[:column1, :column2].from[:table2]
end
assert_equal "insert into table_name select column1, column2 from table2", statement.to_sql
end
The above code uses both the Insert and Select classes within the test. Using both classes does produce a passing test. However, a more robust implementation would allow the behavior of Select to be changed without breaking any of the tests within Insert. The next several entries will focus on the various types of stubs I've used in the past to solve this issue.

Ruby Stub Variations: OpenStruct
Ruby Stub Variations: TestStub
Ruby Stub Variations: Struct
Ruby Stub Variations: Stubba

Friday, September 08, 2006

Ruby instance_exec aka instance_eval with parameters

Updated: Changed the instance_exec implementation to a better version, also written by Mauricio. See comments for more info.

Recently we were extracting some common behavoir to a class method. The behavior of the methods being defined was similar; however, it did differ slightly so we needed the ability to pass a block to the method. Unfortunately, we needed the block to take a parameter and also execute in the scope of the instance.

For example, the bubble method from my previous example is used to define methods dynamically. We also needed the bubble method to add a string to an array.
class << Object
def bubble(*args, &block)
args.each do |method_name|
define_method(method_name) do
instance_eval(&block) if block_given?
self
end
end
end
end
The above implementation will work for this code:
class Foo
bubble :return_self do
some_array << 'return_self'
end
bubble :return_self2 do
some_array << 'return_self2'
end
end
However, if you could pass a parameter to the block the code could become:
class Foo
bubble :return_self do |method_name|
some_array << method_name.to_s
end
end
Unfortunately, instance_eval does not currently allow you to pass parameters.

In Ruby 1.9 instance_exec should solve this problem; however, for the time being you can find an implementation for 1.8.4 that was written by Mauricio Fernandez.
class Object
module InstanceExecHelper; end
include InstanceExecHelper
def instance_exec(*args, &block)
begin
old_critical, Thread.critical = Thread.critical, true
n = 0
n += 1 while respond_to?(mname="__instance_exec#{n}")
InstanceExecHelper.module_eval{ define_method(mname, &block) }
ensure
Thread.critical = old_critical
end
begin
ret = send(mname, *args)
ensure
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
end
ret
end
end
Using instance_exec allows us to use this implementation for bubble:
class << Object
def bubble(*args, &block)
args.each do |method_name|
define_method(method_name) do
instance_exec(method_name, &block) if block_given?
self
end
end
end
end
This allows us to dry up the Foo code to be:
class Foo
bubble :return_value1, :return_value2 do |method_name|
some_array << method_name
end
end
For more info on instance_exec in Ruby 1.9 check out Mauricio's write-up.

Ruby Asserting Equality with True

Bill Caputo wrote a blog entry several months ago stating that he preferred tests that assert equality over tests that simply assert truth. If I remember correctly the main point was that an error message such as 1 expected but was 2 is much more descriptive than false is not true.

I agree with Bill and think it's a great guideline to follow when writing tests. Unfortunately, there are also times when I need to assert truth. These tests usually appear when I need to test a method that should return true or false. In these situations I've found it valuable to use assert_equal instead of assert when programming in Ruby.

The largest reason for this choice is because Ruby treats everything that is not nil or false as true. Therefore, if you are testing the coded? method and you have an implementation error such as:
def coded?
state = :coded
end
Your test will still pass if the test is coded as:
def test_coded?
assert obj.coded?
end
However, this test will fail:
def test_coded?
assert_equal true, obj.coded?
end
Clearly, you should also have a test that verifies that coded? is false; thus, better coverage mitigates this risk. However, I still prefer the assert_equal test implementation because I feel it makes my test suite more robust.

Wednesday, September 06, 2006

Alternative Story Card Format

The common story card format is
As a [ person ]
I would like to [ action ]
so that [ reason for action ]
Unfortunately, I don't believe that it creates the accountability that it attempts to show. For example, a story card could be:
As a script author
I'd like to see a diff of current scripts against previous scripts
so that I can verify alterations
The above story card would be a valid story card; however, it doesn't tell me who introduced this story card. What if the card was introduced by a stakeholder who will actually never play the role of script author. Perhaps it was created in anticipation of an auditing requirement that has yet to be defined. In the end, the functionality may end up YAGNI.

Ownership can be very important when stakeholders need to collaborate on determining scope. Therefore, I prefer explicitly naming the owner of the card.
Jay would like to see a diff of current scripts against previous scripts so that he can verify alterations.
Using this format also ensures that only customers are creating requirements.

For more information on Story Cards check out the c2 wiki.

Tuesday, September 05, 2006

Ruby/Rails Unit Testing in less than 1 second

I previously wrote about removing the database dependency from Ruby on Rails Unit Tests. I'm pleased to say that several months later my team continues to write unit tests that do not require database access. As a result, our unit tests run quite quickly:
Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader
Started
....................................................................................................................................
Finished in 0.555897 seconds.

132 tests, 219 assertions, 0 failures, 0 errors
However, there is another factor that contributes to our test time: Stubba and Mocha. Stubba and Mocha have become tools that I would not want to live without.

Mocha is my new favorite mocking framework. Mocha's syntax is intuative and the mocks are auto-verified.
def test_execute(command='sql string', connection=mock)
connection.expects(:execute).with(command)
SqlExecutor.execute(command, connection)
end
Stubba is similar to Mocha except it adds mock and stub capabilities to concrete classes.
def test_execute(command='sql string')
DatabaseConnection.expects(:execute).with(command)
SqlExecutor.execute(command)
end
Stubba can also be used for easy white box unit testing of specific methods. Stubba allows you to mock some methods of a concrete class while testing the actual implementations of the other methods.
def test_parse(text='any text')
Parser.expects(:preprocess).returns('condition.add')
Builder.expects(:build)
Parser.parse(text)
end
Both Stubba and Mocha allow our tests to be concise and descriptive while increasing test performance.

For more information on removing the database dependency check out this entry and for more info on Stubba and Mocha check out the RubyForge page.