Tuesday, September 8, 2009

Recipe Update Grits

‹prev | My Chain | next›

Yesterday, I created Sinatra helper methods that queried CouchDB views for updated versions of recipes as well as recipes that have been replaced by a more recent recipe in the cookbook. Those helper methods were close to the CouchDB metal. Today I would like to build additional helper methods that can be used directly inside Haml templates.

The first thing that I do is query-replace the previous method names (recipe_update_of, recipes_updated_by) for more CouchDB-centric names (couch_recipe_update_of, couch_recipes_updated_by). This a cue for my future self that these methods are closely related to the underlying CouchDB view. It could also be interpreted as a cue to my current self that these methods might be more properly organized in their own class or module. I will table that latter thought for the time being.

Right now, I want to drive a helper that will indicate that the current recipe is an update to a previous recipe. On the legacy site, that looked like:



Putting on my BDD hat, I write the following RSpec
example:
describe "recipe_update_of" do
it "should include links to previous recipes" do
stub!(:couch_recipe_update_of).and_return(['2000-09-07-recipe'])
recipe_update_of('2009-09-07-recipe').
should have_selector('span.update-of a')
end
end
The first time I run this example, I see a failure due to the lack of a helper method named recipe_update_of:
cstrom@jaynestown:~/repos/eee-code$ spec ./spec/eee_helpers_spec.rb 
.......................................................................F..

1)
NoMethodError in 'recipe_update_of should include links to previous recipes'
undefined method `recipe_update_of' for #<Test::Unit::TestCase::Subclass_13:0xb6b25d5c>
./spec/eee_helpers_spec.rb:501:

Finished in 0.104805 seconds

74 examples, 1 failure
So I define that helper method:
    def recipe_update_of
end
When the examples are run now, I receive a failure for the incorrect arity of the helper method:
cstrom@jaynestown:~/repos/eee-code$ spec ./spec/eee_helpers_spec.rb 
.......................................................................F..

1)
ArgumentError in 'recipe_update_of should include links to previous recipes'
wrong number of arguments (1 for 0)
./spec/eee_helpers_spec.rb:501:in `recipe_update_of'
./spec/eee_helpers_spec.rb:501:

Finished in 0.112535 seconds

74 examples, 1 failure
I add a single argument to the helper method as desired by the example:
    def recipe_update_of(permalink)
end
Now I find no errors, but the expectation is not met:
cstrom@jaynestown:~/repos/eee-code$ spec ./spec/eee_helpers_spec.rb 
.......................................................................F..

1)
'recipe_update_of should include links to previous recipes' FAILED
expected following output to contain a <span.update-of a href='/recipes/2000-09-07-recipe'/> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">

./spec/eee_helpers_spec.rb:501:

Finished in 0.118594 seconds

74 examples, 1 failure
I work methodically through the change-the-message or make-it-pass cycle to ensure that I check my assumptions with each step. With assumptions successfully checked, I am ready to make it pass. That is a fairly in-depth example—I need the links returned by the couch_recipe_update_of helper method to be embedded in an update <span>:
    def recipe_update_of(permalink)
previous = couch_recipe_update_of(permalink)
links = previous.map do |update_permalink|
%Q|<a href="/recipes/#{update_permalink}"></a>|
end

%Q|<span class="update-of">| +
%Q|This is an update of a previous recipe: | +
links.join(", ") +
%Q|</span>|
end
With that, I have all of my examples passing:
cstrom@jaynestown:~/repos/eee-code$ spec ./spec/eee_helpers_spec.rb 
..........................................................................

Finished in 0.172632 seconds

74 examples, 0 failures
The links are in place, but there is no content. To drive the link text, I use this example:
  it "should link to a pretty formatted date" do
stub!(:couch_recipe_update_of).and_return(['2000-09-07-recipe'])
recipe_update_of('2009-09-07-recipe').
should have_selector('span.update-of a',
:content => "September 7, 2000")
end
I get that passing by calculating the date string:
    def recipe_update_of(permalink)
previous = couch_recipe_update_of(permalink)
links = previous.map do |update_permalink|
date_str = Date.parse(update_permalink).strftime("%B %e, %Y")
%Q|<a href="/recipes/#{update_permalink}">#{date_str}</a>|
end

%Q|<span class="update-of">| +
%Q|This is an update of a previous recipe: | +
links.join(", ") +
%Q|</span>|
end
Last up, I write an example that describes the case in which the recipe is not an update to a previous recipe:
  it "should do nothing if this is not an update" do
stub!(:couch_recipe_update_of)
recipe_update_of('2009-09-07-recipe').
should be_nil
end
I can get that passing with a conditional:
    def recipe_update_of(permalink)
previous = couch_recipe_update_of(permalink)
if previous
links = previous.map do |update_permalink|
date_str = Date.parse(update_permalink).strftime("%B %e, %Y")
%Q|<a href="/recipes/#{update_permalink}">#{date_str}</a>|
end

%Q|<span class="update-of">| +
%Q|This is an update of a previous recipe: | +
links.join(", ") +
%Q|</span>|
end
end
That gets all of my specs passing:
cstrom@jaynestown:~/repos/eee-code$ spec ./spec/eee_helpers_spec.rb 
............................................................................

Finished in 0.109127 seconds

76 examples, 0 failures
I follow a similar procedure to drive the this-recipe-has-been-updated feature:



With helper methods in hand, I update my Haml templates to use them:
...
%div
= recipe_updated_by(@recipe['id'])
%div
= recipe_update_of(@recipe['id'])
...
That should just about do it for the "inside" code work. Tomorrow I will work my way back out to the Cucumber scenario to verify (hopefully) that I have done it well.

No comments:

Post a Comment