Monday, September 14, 2009

The Last Helper

‹prev | My Chain | next›

All right, let's see if I can finish this helper off.

I left myself notes about what was left:
cstrom@jaynestown:~/repos/eee-code$ spec -cfs \   
./spec/eee_helpers_spec.rb
...
alternative_preparations
- should retrieve IDs
- should retrieve titles for each alternate ID
- should return nothing if there are no alternate preparations (PENDING: Not Yet Implemented)
- should return a comma separated list of links to recipes (PENDING: Not Yet Implemented)

Pending:

alternative_preparations should return nothing if there are no alternate preparations (Not Yet Implemented)
./spec/eee_helpers_spec.rb:590

alternative_preparations should return a comma separated list of links to recipes (Not Yet Implemented)
./spec/eee_helpers_spec.rb:591

Finished in 0.361926 seconds

85 examples, 0 failures, 2 pending
The "should show nothing" example is a simple one:
  it "should return nothing if there are no alternate preparations" do
alternative_preparations('2009-09-14').
should be_nil
end
In fact, that example passes right away. It is still a good example to have to ensure that I do not break it with future changes, such as:
it "should return a comma separated list of links to recipes" 
Before implementing that example, I need yet another helper—this one to pull back a list of titles, given a list of IDs. It should query the "titles" CouchDB view and it should return a list of hashes. In RSpec format:
describe "couch_recipe_titles" do
it "should retrieve multiple recipe titles, given IDs" do
RestClient.
should_receive(:post).
with(/titles/, '{"keys":["2008-09-14-recipe","2009-09-14-recipe"]}').
and_return('{"rows": [] }')
couch_recipe_titles(%w{2008-09-14-recipe 2009-09-14-recipe})
end

it "should return a list of recipe IDs and titles" do
RestClient.
stub!(:post).
and_return <<"_JSON"
{"total_rows":578,"offset":175,"rows":[
{"id":"2008-09-14-recipe","key":"2008-09-14-recipe","value":"Recipe #1"},
{"id":"2009-09-14-recipe","key":"2009-09-14-recipe","value":"Recipe #2"}
]}
_JSON

couch_recipe_titles(%w{2008-09-14-recipe 2009-09-14-recipe}).
should == [
{:id => "2008-09-14-recipe", :title => "Recipe #1"},
{:id => "2009-09-14-recipe", :title => "Recipe #2"}
]

end
end
The POSTs in those examples with the JSON payload are an example of how CouchDB allows lookup by multiple keys in it views (such as the title view that is being used here).

Yes, those examples are very much tied to current implementation. I am not writing good regression tests here (that is what Cucumber will do). I am simply trying to drive implementation. Said implementation is nice and clean—pull back the JSON results and map the results into a list of hashes:
    def couch_recipe_titles(ids)
data = RestClient.post "#{_db}/_design/recipes/_view/titles",
%Q|{"keys":[#{ids.map{|id| "\"#{id}\""}.join(',')}]}|

JSON.parse(data)['rows'].map do |recipe|
{ :id => recipe["id"], :title => recipe["value"] }
end
end
With the couch_recipe_titles helper, together with the couch_alternatives, I am ready to polish off the alternative_preparations helper. First up, establishing the context of a recipe with two alternative preparations:
  context "recipe with two alternate preparations" do
before(:each) do
stub!(:couch_alternatives).
and_return(%w{2007-09-14-recipe 2008-09-14-recipe})
stub!(:couch_recipe_titles).
and_return([
{:id => "2007-09-14-recipe", :title => "Recipe #1"},
{:id => "2008-09-14-recipe", :title => "Recipe #2"}
])
end
The two CouchDB helpers return two IDs and two recipes in this context. The first aspect of the helper that I would like describe here is that there ought to be two links:
    it "should have two links" do
alternative_preparations('2009-09-14').
should have_selector("a", :count => 2)
end
To make that example pass, while still ensuring that the no-alternatives case pass, I add a conditional on the return value of the alternate preparations lookup, and then map the recipes associated with those IDs to <a> tags:
    def alternative_preparations(permalink)
ids = couch_alternatives(permalink)
if ids && ids.size > 0
couch_recipe_titles(ids).
map{ |recipe| %Q|<a href="/recipes/#{recipe[:id]}">#{recipe[:title]}</a>|}.
join(", ")
end
end
The last thing that I need this helper to do is to include a label:
    it "should label the alternate preparations as such" do
alternative_preparations('2009-09-14').
should contain("Alternate Preparations:")
end
Making that pass is a simple matter of adding the label:
    def alternative_preparations(permalink)
ids = couch_alternatives(permalink)
if ids && ids.size > 0
%Q|<span class="label">Alternate Preparations:</span> | +
couch_recipe_titles(ids).
map{ |recipe| %Q|<a href="/recipes/#{recipe[:id]}">#{recipe[:title]}</a>|}.
join(", ")
end
end
Phew! That combination of helpers ended up taking me much longer than I originally anticipated, but I think that I finally have it done. Not satisfied with "thinking" that I am done, I will verify that I am done by working my way back out to the Cucumber scenario. Tomorrow.

No comments:

Post a Comment