Sunday, September 13, 2009

Alternate Preparations Helper

‹prev | My Chain | next›

Today, I continue working on the alternate recipe preparations. After a small spike yesterday, I know how I would like to access the lists of alternate preparations for any given recipe (via a recipes/alternatives view) and how the results should look:
cstrom@jaynestown:~/repos/eee-code$ curl \
http://localhost:5984/eee/_design/recipes/_view/alternatives\?key=\"2004-02-04-squid\"
{"total_rows":8,"offset":5,"rows":[
{"id":"d16f491629b78a5bb13adb49ec6bee00",
"key":"2004-02-04-squid",
"value":["2002-08-12-sauce",
"2003-01-17-sausage_pasta",
"2003-02-13-pasta",
"2003-02-16-sauce",
"2003-04-07-cavatelli",
"2004-04-26-sauce",
"2006-02-14-sauce"]
}
I have been dumping code into my Sinatra helpers for some time now and it is beginning to show. I have reached 300 lines of code in helpers.rb. Making matters worse, I have code that is not directly used in the Sinatra app or the Haml templates. I may need to refactor for better cohesion. Before getting it right, I will get it done.

First up, I write an RSpec example describing that a helper method should ask CouchDB for alternate preparations:
describe "couch_alternatives" do 
it "should ask CouchDB for alternates" do
RestClient.
should_receive(:get).
with(/alternatives.+key=.+2000-09-13-recipe/).
and_return('{"rows": [] }')
couch_recipe_updated_by('2000-09-13-recipe')
end
end
After adding code to resolve failures for no such method, wrong number of arguments and not receiving the expected arguments, I end up with this helper:
    def couch_alternatives(permalink)
url = "#{_db}/_design/recipes/_view/alternatives?key=%22#{permalink}%22"
RestClient.get url
end
That will return JSON, which is going to be too helpful. I would rather return the results of the "rows" attribute above:
  it "should be a list of IDs" do
RestClient.
stub!(:get).
and_return('{"rows": [{"value": ["2000-09-13-recipe"]}] }')

couch_alternatives('2009-09-12-recipe').
should == ['2000-09-13-recipe']
end
As expected, that fails because the function still returns JSON:
cstrom@jaynestown:~/repos/eee-code$ spec ./spec/eee_helpers_spec.rb 
................................................................................F

1)
'couch_alternatives should be a list of IDs' FAILED
expected: ["2000-09-13-recipe"],
got: "{\"rows\": [{\"value\": [\"2000-09-13-recipe\"]}] }" (using ==)
./spec/eee_helpers_spec.rb:568:

Finished in 0.113826 seconds

81 examples, 1 failure
To get that passing, I store the JSON in a local variable and parse it:
    def couch_alternatives(permalink)
url = "#{_db}/_design/recipes/_view/alternatives?key=%22#{permalink}%22"
data = RestClient.get url
results = JSON.parse(data)['rows']
results.first && results.first['value']
end
Next up is the actual helper that will be used in the Haml templates, which I will call alternate_preparations. The ultimate goal of this helper is a comma separated list of recipe links, when alternate preparations are present:
describe "alternative_preparations" do
it "should return nothing if there are no alternate preparations"
it "should return a comma separated list of links to recipes"
end
First I need to use the couch_alternates helper to pull back the IDs:
  it "should retrieve IDs" do
should_receive(:couch_alternatives).and_return([])
alternative_preparations('2009-09-13')
end
After implementing that, I need to post JSON keys to CouchDB to pull back a list of recipe titles:
  it "should retrieve titles for each alternate ID" do
RestClient.
should_receive(:post).
with(/titles/, '{"keys":["2000-09-13-recipe","2001-09-13-recipe"]}').
and_return('{"rows": [] }')
alternative_preparations('2009-09-13')
end
I make that pass with:
    def alternative_preparations(permalink)
ids = couch_alternatives(permalink)
recipes = RestClient.post "#{_db}/_design/recipes/_view/titles",
%Q|{"keys":[#{ids.map{|id| "\"#{id}\""}.join(',')}]}|

end
It took a while to get that example passing because the example is so tied to implementation of the JSON output (no spaces, double quotes, etc.). I stop there for the day. I will pick back up completing the alternative_preparations helper tomorrow and, hopefully, working my way back out to the Cucumber scenario describing this feature.

No comments:

Post a Comment