Thursday, June 18, 2009

DRYing up the Categories

‹prev | My Chain | next›

I'm on vacation, but the don't-break-the-chain gods recognize no holidays and never spend a day at the beach. Lest they gaze upon me with disfavor, I will continue to do at least a little work each day on my chain to stay in their good graces. I have no idea what the wrath of the don't-break-the-chain gods is like and I do not intend to find out.

I noted last night that the code for the category links at the top of most pages was starting to feel repetitive. On the legacy Rails site, the category links look like:



The "Vegetarian" category is in red to indicate that the user is on a vegetarian meal, recipe, or list.

The new implementation of recipe categories, using Haml, looks like:
%ul#eee-categories
%li= recipe_category_link(@recipe, 'Italian')
%li= recipe_category_link(@recipe, 'Asian')
%li= recipe_category_link(@recipe, 'Latin')
%li= recipe_category_link(@recipe, 'Breakfast')
%li= recipe_category_link(@recipe, 'Chicken')
%li= recipe_category_link(@recipe, 'Fish')
%li= recipe_category_link(@recipe, 'Meat')
%li= recipe_category_link(@recipe, 'Salad')
%li= recipe_category_link(@recipe, 'Vegetarian')
%li
%a Recipes
There is much repetition in there—9 calls of the recipe_category_link helper, which is responsible for highlighting the link when appropriate. What's worse is that the following is in the meal Haml template (the above is in the recipe template):
%ul#eee-categories
%li= recipe_category_link(@recipes, 'Italian')
%li= recipe_category_link(@recipes, 'Asian')
%li= recipe_category_link(@recipes, 'Latin')
%li= recipe_category_link(@recipes, 'Breakfast')
%li= recipe_category_link(@recipes, 'Chicken')
%li= recipe_category_link(@recipes, 'Fish')
%li= recipe_category_link(@recipes, 'Meat')
%li= recipe_category_link(@recipes, 'Salad')
%li= recipe_category_link(@recipes, 'Vegetarian')
%li
%a Recipes
I have definitely reached the refactor portion of the Red-Green-Refactor behavior driven development cycle. I could DRY the code up by either factoring the duplication out into a shared partial or into a helper. I opt for the helper since recipe_category_link is already a helper—might as well keep them close should any later work need to be done.

The specs for the categories helper end up reading:
categories
- should link to the italian category
- should be able to highlight the link to the italian category
- should link to the fish category
- should link to the vegetarian category
- should link to all recipes
Since there is so much repetition in the the category listing, the individual RSpec examples are all very much the same. The "vegetarian" example reads:
  it "should link to the vegetarian category" do
categories({}).
should have_selector("#eee-categories a", :content => "Vegetarian")
end
The other examples replace "vegetarian" with the appropriate other category.

The categories helper gets driven to:
    def categories(context)
categories = %w{Italian Asian Latin Breakfast Chicken Fish Meat Salad Vegetarian}

links = categories.map do |category|
%Q|<li>#{recipe_category_link(context, category)}</li>|
end

links << "<a>Recipes</a>"

%Q|<ul id="eee-categories">#{links}</ul>|
end
Finally, I replace the repetitive Haml code with a single call to the new categories helper. I make sure to run all of my specs and then call it a night.
(commit)

No comments:

Post a Comment