RSimpy
I am currently wrapping up an initial release of a new gem that wraps the Simpy.com API. I like social bookmarking and used delicious for years before switching to Simpy as my primary service. Simpy is not the biggest online bookmarking service, but I’ve decided to use it for reasons I will outline in another post. In any case I’ve put together a gem that wraps the API with the help of Httparty. It actually turned out much simpler than I expected it to be, httparty did all of the heavy lifting, but again that is for another post.
So, I have a functioning gem for Simpy, now I want to make something useful with it. The reason I made the gem was to simplify my bookmarking which is overall a mess on multiple workstations in multiple locations. Since I’ve recently been getting familia with cron, I’ve decided to make a script to store urls locally and use cron to run a script that pushes a file of pages to Simpy.
As I touched on briefly, I’ve been unhappy with all my previous bookmarking solutions thus far. This time I want to make it, dead simple. I don’t want to have to enter any additional information if I don’t want to, just a url and have it go on it’s merry way. However to post the link, I will need to at least have the page title. Pulling a page title from a url is really outside of the scope of RSimpy. Ruby makes it fairly simple to do, but I don’t want to isolate the functionality to a script. Ruminate to the rescue, but what is Ruminate?
Ruminate
Well the answer to that question is that I’m not really sure. A few months back, I made a gem with jeweler called ruminate with the description “Extracts statistics from html documents”. Actually, I’m not really sure what I originally had in mind when I setup this gem, but it will be useful now since I want to extract at least the page title. Once I get the ability to extract a page title, I can make a script that is simply glue code to put Ruminate together with RSimpy and voila!
Bootstrapping the project
I have a vague idea of something that I want Ruminate to do, that is I want to be able to query a url to return the title of the page. It’s obviously not the entire purpose of the gem, but I’m sure that will be lots of other bits that will come together in time.
The question is, How do I kickstart this project with TDD to get it off the ground?
Programming by Intention
Programming by intention is the practice of writing code, while pretending that any methods or classes you might reference already exist and operate in the way that you reference them. It’s a great way of uncovering a simple intuative API that you might otherwise would have missed.
I’ve seen it described in two books, first in Everyday Scripting with Ruby, from The Pragmatic Bookshelf. Secondly, it’s used in an excellent book I’m currently reading called Test Driven from Manning.
There are a number of sources stating that TDD is about design first and automated testing is a nice by-product. In my limited experience with TDD, programming by intention is the crux of this design, and by extension the primary purpose of TDD.
Cucumber
One of the things that I really like about cucumber is that you can plainly state your intent and important bits of your feature focused domain in plain English and match that up to executable code. To describe the ability of Ruminate to grab the title of a url, I made the following test:
Feature: Get Page Title
In to get the page title
A user of ruminate
Will request a page title
Scenario: Requesting the title of a page from the page
Given the url "http://www.google.com"
When I execute the request
Then the "title" should be "Google"
I let cucumber auto-generate the regular expression blocks and came up with the following steps:
Given /^the url "([^\"]*)"$/ do |url|
@url = url
end
When /^I execute the request$/ do
query = "Select title from #{@url};"
@result = chew query
end
Then /^the "([^\"]*)" should be "([^\"]*)"$/ do |msg, value|
assert_equal value, @result
end
As you can see I’ve decided to try out a sql like syntax for the tool. I thought it sounded interesting, we’ll see if I keep it. In any case, I now need to make this test pass with the help of a mixin, that I’m including in features/support/env.rb
# features/support/env.rb
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
require 'ruminate'
include Ruminate
require 'test/unit/assertions'
World(Test::Unit::Assertions)
# lib/ruminate.rb
require 'query_parser'
require 'engine'
module Ruminate
def chew query
parser = Ruminate::QueryParser.new
query_object = parser.parse query
engine = Ruminate::Engine.new
engine.execute query_object
end
end
Here I’ve used the mixin to split the method into to operations and encapsulated each of these into a class. Programming by intention has now generated a mixin and two classes that have a clean separation of responsibilities. Here is the simplest thing that could possibly work to make the feature pass.
# lib/query_parser.rb
module Ruminate
class QueryParser
def parse query
nil
end
end
end
# lib/engine.rb
module Ruminate
class Engine
def execute query
"Google"
end
end
end
Obviously this isn’t the final code, but it gives us the minimal implementation to pass the feature. Now I can turn to Test::Unit and shoulda to flesh out tests and implementations to make it work in a more useful manner.
What I really wanted to show here is how to get a TDD project started. I know that I’ve struggled with this in the past. What to test first can be a big decision, one that can create a type of code writers block. When this moment happened where everything just came together and I was able to generate a mixin and two orthagonal classes with a simple feature, I wanted to write it up.
The Code
The full code for this example can be found on github with the tag simplest-thing-that-could-possibly-work