Saturday, April 11, 2009

Some Recipe Detail Clean-Up

‹prev | My Chain | next›

At this point, I have the entire Recipe Details feature (5 scenarios and 20 steps) passing:
cstrom@jaynestown:~/repos/eee-code$ cucumber features/recipe_details.feature -n 
Feature: Recipe Details

So that I can accurately reproduce a recipe at home
As a web user
I want to be able to easily recognize important details
Scenario: Viewing a recipe with several ingredients
Given a recipe for Buttermilk Chocolate Chip Pancakes
When I view the recipe
Then I should see an ingredient of "1 cup all-purpose, unbleached flour"
And I should see an ingredient of "¼ teaspoons salt"
And I should see an ingredient of "chocolate chips (Nestle Tollhouse)"

Scenario: Viewing a recipe with non-active prep time
Given a recipe for Crockpot Lentil Andouille Soup
When I view the recipe
Then I should see 15 minutes of prep time
And I should see that it requires 5 hours of non-active cook time

Scenario: Viewing a list of tools used to prepare the recipe
Given a recipe for Chicken Noodle Soup
When I view the recipe
Then I should see that it requires a Bowl, a Colander, a Cutting Board, a Pot and a Skimmer to prepare

Scenario: Main site categories
Given a recipe for Mango and Tomato Salad
When I view the recipe
Then I should see the site-wide categories of Italian, Asian, Latin, Breakfast, Chicken, Fish, Meat, Salad, and Vegetarian
And the Salad and Vegetarian categories should be active

Scenario: Viewing summary and recipe instructions
Given a recipe for Curried Shrimp
When I view the recipe
Then I should a nice summary of the dish
And I should see detailed, easy-to-read instructions


5 scenarios
20 steps passed
Inside the application, I have 30 examples passing:
cstrom@jaynestown:~/repos/eee-code$ rake
(in /home/cstrom/repos/eee-code)
/usr/bin/ruby1.8 ./spec/eee_spec.rb
...

Finished in 0.073494 seconds

3 examples, 0 failures
/usr/bin/ruby1.8 ./spec/eee_helpers_spec.rb
......

Finished in 0.014949 seconds

6 examples, 0 failures
/usr/bin/ruby1.8 ./spec/views/recipe.haml_spec.rb
.......................

Finished in 0.191058 seconds

23 examples, 0 failures
(commit of the Rakefile).

With all that work done, I have yet to actually look at a recipe in a browser. Now is as good a time as any:



On first inspection, everything looks good, but I realize that I have not linked in the recipe images yet. Scrolling down, I see:



Oops, the parenthetical ingredient brands are showing even when there is no brand. I thought I had accounted for that case, but obviously not. No matter, it should not be difficult to fix. But first, the recipe photo...

Including a Recipe Photo

In the HAML spec, I am mostly interested in the location of the image in the document. The examples that describe this:
  context "a recipe with an image" do
it "should include an image in the recipe summary" do
self.stub!(:image_link).and_return("<img/>")
render("views/recipe.haml")
response.should have_selector("#eee-summary > img")
end

end

context "a recipe without an image" do
it "should not include an image" do
self.stub!(:image_link).and_return(nil)
render("views/recipe.haml")
response.should_not have_selector("#eee-summary > img")
end
end
I am stubbing out a helper method, so the actual implementation is trivial:
#eee-summary
= image_link @recipe
= wiki @recipe['summary']
When I started my red-green-refactor cycle, the responsibility for the image tag was in the HAML template. It got ugly as I started handling boundary conditions, which is how the image_link helper came into being. The URL for images was established towards the end of the recipe details feature. The examples:
describe "image_link" do
it "should return a link tag pointing to the document's image" do
doc = {
'_id' => "foo",
'_attachments' => { 'sample.jpg' => { } }
}

image_link(doc).
should have_selector("img",
:src => "/images/#{doc['_id']}/sample.jpg")
end

it "should return nil if no attachments" do
image_link({ }).should be_nil
end
it "should return nil if no image attachments" do
doc = { '_attachments' => { 'sample.txt' => { } } }
image_link(doc).should be_nil
end
end
And the code that actually implements it:
    def image_link(doc)
return nil unless doc['_attachments']

filename = doc['_attachments'].
keys.
detect{ |f| f =~ /jpg/ }

return nil unless filename

%Q|<img src="/images/#{doc['_id']}/#{filename}"/>|
end
Since I do not have a cucumber scenario, I will just check the output in the browser:

(commit)

Boundary Condition: No Brand

Investigating this, I find that I accounted for the case of when brands are nil, but not empty strings. The empty strings are an artifact of the import from the legacy Rails application. I hate to add code to account for an artifact, but this is fairly trivial. If this is the only workaround, I can live with it. A TODO can serve as a reminder in case more artifacts are found, at which point, a CouchDB migration can be used.

This would be trivial to address with Rails' blank?, but, even without it, it is not too difficult. First, the example (including the TODO):
  # TODO: this should not be necessary.  The blank brands are an
# artifact of the import-from-rails process
context "a recipe with a blank brand" do
it "should not include brand information" do
@recipe['preparations'] = []
@recipe['preparations'] << {
'quantity' => 1,
'unit' => '12 ounce bag',
'brand' => '',
'ingredient' => {
'name' => 'chocolate chips'
}
}

render("views/recipe.haml")

response.should_not have_selector(".ingredient > .brand")
end
end
To implement this example, a Regexp check for any non-space character will work:
        - if preparation['brand'] =~ /\S/
%span.brand
= "(" + preparation['brand'] + ")"
To prevent the nil brand case from breaking, this code relies on the fact that the NilClass does support Regexp matching. Specifically:
>> nil =~ /\S/
=> false
A quick check in the browser confirms that the issue has been resolved—no more blank brands:

(commit)

That should wrap up work on the recipe details. Next up, search, I think.

No comments:

Post a Comment