Sunday, June 14, 2009

Meal Photos and Menu Items

‹prev | My Chain | next›

Today, I continue work on the navigation-from-the-homepage Cucumber scenario. Yesterday, I was able to include meal summaries on the homepage. The next two steps in the scenario are additional "thens" (post-conditions) verifying that a meal thumbnail and menu items are also included on the homepage.

Nothing new needs to be added to the Sinatra application to include either of these pieces of information. This means that work today begins with the Haml specification to drive the new data presentation.

The example of how I'd like the thumbnail to display:
  it "should include a thumbnail image of the meal" do
render("/views/index.haml")
response.should have_selector("img",
:src => "/images/2009-05-15/image1.jpg",
:width => "200",
:height => "150")
end
I already have an image_link helper, so I can make this example pass with a simple call to that helper:
    = image_link meal, :width => 200, :height => 150
Next, I need two examples describing the presentation of each meal's menu items on the homepage. They should be wikified and comma separated:
  it "should include a comma separated list of menu items" do
assigns[:meals][0]["menu"] << "chips"
assigns[:meals][0]["menu"] << "salsa"
render("/views/index.haml")
response.should have_selector(".menu-items",
:content => "chips, salsa")
end

it "should wikify menu items" do
assigns[:meals][0]["menu"] << "_really_ hot salsa"
render("/views/index.haml")
response.should have_selector(".menu-items em",
:content => "really")
end
The entire index.haml template that implements this:
.meals
%h1 Meals
- @meals.each do |meal|
= image_link meal, :width => 200, :height => 150
%h2= meal["title"]
= wiki(meal["summary"])
.menu-items= wiki(meal["menu"].join(", "))
With that, I am done with my inside/code work for today. Working my was back out to the Cucumber scenario, I need to include an image attachment if I expect to include it in the homepage:
Given /^(\d+) yummy meals$/ do |count|
start_date = Date.new(2009, 6, 11)

@meal_ids = (0...count.to_i).inject([]) do |memo, i|
date = start_date - (i * 10)

meal = {
:title => "Meal #{i}",
:date => date.to_s,
:serves => 4,
:summary => "meal summary",
:description => "meal description",
:type => "Meal",
:menu => [],
:_attachments => { "meal#{i}.jpg" => {
:data => "R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAEBMgA7",
:content_type => "image/gif"}

}
}

RestClient.put "#{@@db}/#{date.to_s}",
meal.to_json,
:content_type => 'application/json'

memo + [date.to_s]
end
end
The content is the base64 encoded content of good old 1x1 spacer.gif. It does not matter much what the content is for the purposes of the Cucumber scenario—just that the filename ends with "jpg" (implementation detail of the image_link helper). With images attached to each meal, I can verify this Cucumber step with:
Then /^the prominently displayed meals should include a thumbnail image$/ do
response.should have_selector("img", :count => 10)
end
That's a good stopping point for tonight. Tomorrow, I will mark off the meals-should-include-recipe-titles step as complete. I already have enough code to implement that step, but have a kink or two that needs working out before I consider it really done.

Saturday, June 13, 2009

One Step Closer to a Homepage

‹prev | My Chain | next›

Today, I pick up midway through a step in the Cucumber scenario describing site exploration from the homepage. Before I can move back out to the scenario to check off the step as complete, I need to elaborate some more on the current step: "I should see the 10 most recent meals prominently displayed". The meal titles are already presented on the homepage, but I need to display the summary as well.

The example (in RSpec format) that describes the presentation of the titles on the homepage:
describe "index.haml" do
before(:each) do
assigns[:meals] = [{ "key" => "2009-05-15",
"value" => ['2009-05-15', "Foo"] },
{ "key" => "2009-05-31",
"value" => ["2009-05-31", "Bar"] }]
end

it "should link to the meal titles" do
render("/views/index.haml")
response.should have_selector("h2", :content => "Bar")
end
end
The @meals instance variable is assigned by the Sinatra controller, which retrieves the data structure from a CouchDB view. When I first created the view, I only needed the date and title in the value. Now I need the summary as well. Peeking ahead in the Cucumber scenario, I see that I will need the meal image and menu items as well:
  Scenario: Quickly scanning meals and recipes accessible from the home page
Given 25 yummy meals
And 1 delicious recipe for each meal
And the first 5 recipes are Italian
And the second 10 recipes are Vegetarian
When I view the site's homepage
Then I should see the 10 most recent meals prominently displayed
And the prominently displayed meals should include a thumbnail image
And the prominently displayed meals should include the recipe titles

...
So I need nearly the entire meal. Do I pull the entire meal into the CouchDB view or do I look up each meal individually? I think I will go with the latter. My convention with CouchDB views has been to include only the date. When I implemented the between meal navigation, I added the title. If I switch now to include an entire object, I may end up causing confusion down the line.

So I need to stub out the initial RestClient.get to the CouchDB view to pull back 13 meals (ten for prominent display, 3 more for text links only). The example usage then attempts to verify that the detailed meal information is only request ten times (for prominent display). Thirteen meals make for a long example block:
      it "should pull back full details for the first 10 meals" do
RestClient.
stub!(:get).
and_return('{"rows": [' +
'{"key":"2009-06-10","value":["2009-06-10","Foo"]},' +
'{"key":"2009-06-09","value":["2009-06-09","Foo"]},' +
'{"key":"2009-06-08","value":["2009-06-08","Foo"]},' +
'{"key":"2009-06-07","value":["2009-06-07","Foo"]},' +
'{"key":"2009-06-06","value":["2009-06-06","Foo"]},' +
'{"key":"2009-06-05","value":["2009-06-05","Foo"]},' +
'{"key":"2009-06-04","value":["2009-06-04","Foo"]},' +
'{"key":"2009-06-03","value":["2009-06-03","Foo"]},' +
'{"key":"2009-06-02","value":["2009-06-02","Foo"]},' +
'{"key":"2009-06-01","value":["2009-06-01","Foo"]},' +
'{"key":"2009-05-31","value":["2009-05-31","Foo"]},' +
'{"key":"2009-05-30","value":["2009-05-30","Foo"]},' +
'{"key":"2009-05-29","value":["2009-05-29","Foo"]}' +
']}')

RestClient.
should_receive(:get).
with(/2009-0/).
exactly(10).times.
and_return('{"title":"foo",' +
'"summary":"foo summary",' +
'"menu":[]}')

get "/"

end
end
With the example now failing, I use a range to inject 10 IDs into a meal array for the Haml template:
get '/' do
url = "#{@@db}/_design/meals/_view/by_date?limit=13"
data = RestClient.get url
@meal_view = JSON.parse(data)['rows']

@meals = @meal_view[0...10].inject([]) do |memo, couch_rec|
data = RestClient.get "#{@@db}/#{couch_rec['key']}"
meal = JSON.parse(data)
memo + [meal]
end


haml :index
end
With the Sinatra application behaving as desired, now I need to get the Haml template to present the new data. After updating the before block and the first example to work with the list of full meals, I add two examples describing the summary, checking for presence and wiki-fication:
  it "should include a summary of the meals" do
render("/views/index.haml")
response.should have_selector("p", :content => "Bar summary")
end

it "should wikify the summary" do
assigns[:meals][0]["summary"] = "Foo *bar* baz"
render("/views/index.haml")
response.should have_selector("p strong",
:content => "bar")

end
One line of Haml is needed to make both example pass:
.meals
%h1 Meals
- @meals.each do |meal|
%h2= meal["title"]
%p= wiki(meal["summary"])
With that, I am ready to check this of my Cucumber list by defining the step:
Then /^I should see the 10 most recent meals prominently displayed$/ do
response.should have_selector("h2", :count => 10)
response.should have_selector("h2", :content => "Meal 0")
response.should have_selector("h2", :content => "Meal 9")
response.should_not have_selector("h2", :content => "Meal 10")
end
Running the Cucumber scenario, I find:
cstrom@jaynestown:~/repos/eee-code$ cucumber -ni features \
-s "Quickly scanning meals and recipes accessible from the home page"
Sinatra::Test is deprecated; use Rack::Test instead.
Feature: Site

So that I may explore many wonderful recipes and see the meals in which they were served
As someone interested in cooking
I want to be able to easily explore this awesome site

Scenario: Quickly scanning meals and recipes accessible from the home page
Given 25 yummy meals
And 1 delicious recipe for each meal
And the first 5 recipes are Italian
And the second 10 recipes are Vegetarian
When I view the site's homepage
Then I should see the 10 most recent meals prominently displayed
expected following output to contain a <h2>Meal 0</h2> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><div class="meals">
<h1>Meals</h1>
<h2>Meal 24</h2>
<p></p>
<p>meal summary</p>
<h2>Meal 23</h2>
<p></p>
<p>meal summary</p>
<h2>Meal 22</h2>
<p></p>
<p>meal summary</p>
<h2>Meal 21</h2>
<p></p>
<p>meal summary</p>
<h2>Meal 20</h2>
<p></p>
<p>meal summary</p>
<h2>Meal 19</h2>
<p></p>
...
Aw man!

After recovering from the initial disappointment, I look closer at the failure to discover that the meals are being returned from the CouchDB view in the opposite order that I wanted. This is a hazard of extensive stubbing in my unit tests. Thankfully, Cucumber caught the problem for me. Adding a descending=true to my GET of the CouchDB view should resolve the problem:
get '/' do
url = "#{@@db}/_design/meals/_view/by_date?limit=13&descending=true"
data = RestClient.get url
@meal_view = JSON.parse(data)['rows']

@meals = @meal_view[0...10].inject([]) do |memo, couch_rec|
data = RestClient.get "#{@@db}/#{couch_rec['key']}"
meal = JSON.parse(data)
memo + [meal]
end

haml :index
end
And running the scenario again, I find that everything is working as desired:



One step closer to a working homepage. Yay!

Friday, June 12, 2009

Finally, a Homepage

‹prev | My Chain | next›

Continuing the homepage-and-beyond Cucumber scenario, next up is adding some categories to the recipes:
  Scenario: Quickly scanning meals and recipes accessible from the home page
Given 25 yummy meals
And 1 delicious recipe for each meal
And the first 5 recipes are Italian
And the second 10 recipes are Vegetarian

When I view the site's homepage
...
In the "1 delicious recipe for each meal" step (implemented last night), the recipe IDs are stored away in an instance variable (@recipe_ids) for later use. I make use of it when adding the first five recipes to the "Italian" category:
Given /^the first 5 recipes are Italian$/ do
@recipe_ids[0...5].each do |recipe_id|
data = RestClient.get "#{@@db}/#{recipe_id}"
recipe = JSON.parse(data)

recipe['tag_names'] = ['italian']

RestClient.put "#{@@db}/#{recipe['_id']}",
recipe.to_json,
:content_type => 'application/json'
end
end
That step operates on each of the first five recipes (0...5 includes 0, 1, 2, 3, and 4 and not 5—two dots instead of three would include 5). For each recipe created in the previous Cucumber step, the recipe's tag names are set to 'italian'. The recipe is then PUT back onto the CouchDB database. As with the meal updates from yesterday, this CouchDB PUT is only possible because the lookup pulls back the most recent revision ID.

I implement a similar step for the next 10 vegetarian recipes, then it is time to visit homepage:
When /^I view the site's homepage$/ do
visit('/')
response.should be_ok
end
This step fails, of course, because I have not created the homepage. So let's move into the code to create it.

I drive the initial development of the homepage with these examples:
    describe "GET /" do
it "should respond OK" do
get "/"
last_response.should be_ok
end

it "should request the most recent 13 meals from CouchDB" do
RestClient.
should_receive(:get).
with(/by_date.+limit=13/).
and_return('{"rows": [] }')

get "/"
end
end
Those two examples ensure that the homepage will respond OK and that it will load 13 meals from CouchDB (10 for pretty display, 3 extra for links only). The code that makes these two examples pass is:
get '/' do
url = "#{@@db}/_design/meals/_view/by_date?limit=13"
data = RestClient.get url
@meals = JSON.parse(data)['rows']

""
end
Next, I remove the empty string from the end of the get "/" block, replacing it with a Haml template call:
  haml :index
So now, it is time to move down into the Haml template. I carry the instance variable @meals down to the Haml template to drive the presentation:
describe "index.haml" do
before(:each) do
assigns[:meals] = [{ "key" => "2009-05-15",
"value" => ['2009-05-15', "Foo"] },
{ "key" => "2009-05-31",
"value" => ["2009-05-31", "Bar"] }]
end

it "should link to the meal titles" do
render("/views/index.haml")
response.should have_selector("h2", :content => "Bar")
end
end
Finally, I implement this in a Haml template:
.meals
%h1 Meals
- @meals.each do |meal|
%h2= meal["value"][1]
That is decent progress for a day. Before quitting, I double-check that the Cucumber step describing accessing the homepage is now passing, which it is:



I could make the first "then" step pass. The h2 headers in the Haml template could be construed as "prominently displayed" meals. I still need to display the meal summary, so I will pick up with that tomorrow, before marking that step as complete.

Thursday, June 11, 2009

Outside the Homepage

‹prev | My Chain | next›

I finished off the navigation between meals Cucumber scenario last night. Before moving on, I have to ask myself, "did I create a navigation between recipes scenario?" Sadly, the answer to that is "no", so I do so now:
    Scenario: Navigating to other recipes

Given a "Spaghetti" recipe from May 30, 2009
And a "Pizza" recipe from June 1, 2009
And a "Peanut Butter and Jelly" recipe from June 11, 2009
When I view the "Peanut Butter and Jelly" recipe
Then I should see the "Peanut Butter and Jelly" title
When I click "Pizza"
Then I should see the "Pizza" title
When I click "Spaghetti"
Then I should see the "Spaghetti" title
When I click "Pizza"
Then I should see the "Pizza" title
When I click "Peanut Butter and Jelly"
Then I should see the "Peanut Butter and Jelly" title
That's very nearly a cut-n-paste of the navigation between meals scenario. Hopefully the work already done to implement the latter will make this new scenario easy to implement.

I will not be implementing that today. Even with the new scenario, I have 170 out of 220 scenario steps complete. Most of the remaining scenarios deal with the homepage and site-wide navigation. If I can get those complete, I can deploy—if only in beta. I can live without between-recipe navigation for a beta deployment. Heck, I can live without it in live code for a little while.

So it's on to the "Site" feature. To put myself in the right frame of mind, I revisit the Cucumber preamble that I wrote for this feature:
  So that I may explore many wonderful recipes and see the meals in which they were served
As someone interested in cooking
I want to be able to easily explore this awesome site
Ah, effusive language really inspires. I must do well by these users!

The first scenario in there is "Quickly scanning meals and recipes accessible from the home page". The "given" preconditions are:
    Given 25 yummy meals
And 1 delicious recipe for each meal
And the first 5 recipes are Italian
And the second 10 recipes are Vegetarian
When creating the 25 meals, I inject the IDs into an instance variable for use in later steps:
Given /^(\d+) yummy meals$/ do |count|
start_date = Date.new(2009, 6, 11)

@meal_ids = (0...count.to_i).inject([]) do |memo, i|
date = start_date - (i * 10)

meal = {
:title => "Meal #{i}",
:date => date.to_s,
:serves => 4,
:summary => "meal summary",
:description => "meal description",
:type => "Meal",
:menu => []
}

RestClient.put "#{@@db}/#{date.to_s}",
meal.to_json,
:content_type => 'application/json'

memo + [date.to_s]
end
end
I do something similar when creating a recipe for each meal. The only difference is that I need to update the meal with the recipe included on the menu:
Given /^1 delicious recipe for each meal$/ do
@recipe_ids = @meal_ids.inject([]) do |memo, meal_id|
data = RestClient.get "#{@@db}/#{meal_id}"
meal = JSON.parse(data)

permalink = meal['date'] + "-recipe"

recipe = {
:title => "Recipe for #{meal['title']}",
:date => meal['date'],
:preparations => [
{ 'ingredient' => { 'name' => 'ingredient' } }
]
}

RestClient.put "#{@@db}/#{permalink}",
recipe.to_json,
:content_type => 'application/json'

# Update the meal to include the recipe in the menu
meal['menu'] << "[recipe:#{permalink.gsub(/-/, '/')}"
RestClient.put "#{@@db}/#{meal['_id']}",
meal.to_json,
:content_type => 'application/json'

memo + [permalink]
end
end
Per the CouchDB API I can PUT the meal there because it includes the rev attribute when it is looked up at the beginning of this step. Had I simply remembered the JSON from the meal creation step, I would have gotten a 409 error from CouchDB telling me that the PUT operation failed. By retrieving the meals via the IDs, I retrieve the current revision number, allowing this step to pass without error.

I need to call it a night at this point. I will pick up the next pending step tomorrow and then start working my way into the homepage code.
(commit)

Wednesday, June 10, 2009

RSpec Between Meals

‹prev | My Chain | next›

First up tonight, I need to link to more human readable text when navigating between meals. So I add the following RSpec example to drive this:
  it "should link to easily human readable previous meal" do
render("/views/meal.haml")
response.should have_selector("a", :content => "May 15, 2009")
end
I can make this example pass with some help from Date's strftime:
%div.navigation
‹
=link_to_adjacent_view_date(@meal['date'], @meals_by_date, :previous => true) { |d| Date.parse(d).strftime("%B %e, %Y") }
|
=link_to_adjacent_view_date(@meal['date'], @meals_by_date) { |d| d.strftime("%B %e, %Y") }
›
(my link_to_adjacent_view_date takes an optional block argument that produces the link text).

With that I have reached the refactor portion of the red-green-refactor cycle. I target these two examples for refactoring:
  it "should link to previous meal" do
render("/views/meal.haml")
response.should have_selector("a", :href => "/meals/2009/05/15")
end

it "should link to easily human readable previous meal" do
render("/views/meal.haml")
response.should have_selector("a", :content => "May 15, 2009")
end
I can combine those into a single, easier to read spec:
  it "should link to previous meal" do
render("/views/meal.haml")
response.should have_selector("a",
:href => "/meals/2009/05/15",
:content => "May 15, 2009")
end
So, is time to move back out to the Cucumber scenario? Not quite, the current failing step is trying to click on the meal name, not the date as I have just implemented.

The difficulty with this is that the link_to_adjacent_view_date helper was implemented to yield just the date for presentation. In this case, I need the title as well. That information is in the CouchDB view from last night, which has data that looks like:
{"total_rows":7,"offset":0,"rows":[
{"id":"2002-04-21","key":"2002/04/21","value":["2002-04-21","Sausage Vegetable Grits"]},
{"id":"2002-04-23","key":"2002/04/23","value":["2002-04-23","Black Bean Chili and Fried Grits"]},
{"id":"2002-07-06","key":"2002/07/06","value":["2002-07-06","Mac 'n' Cheese"]},
{"id":"2003-02-09","key":"2003/02/09","value":["2003-02-09","What To Do with Leftover Bubba's"]},
{"id":"2003-05-26","key":"2003/05/26","value":["2003-05-26","Biscuits and Gravy"]},
{"id":"2003-09-24","key":"2003/09/24","value":["2003-09-24","Almost French Onion Soup"]},
{"id":"2006-05-03","key":"2006/05/03","value":["2006-05-03","The Bestest Dinner Ever"]}
]}
The key is the date of meal, the value is a two element array of the id and title. So, yielding both key and value ought to allow me to retain the current date linking behavior (with some modifications), but also support the title linking behavior.

Before I implement this, I have to ask, do I really need the meal text? I already have navigation between meals working—the user can click on the date link. The user, eh? Thinking back to the feature that is driving this inside work, the user is described as "a person interested in exploring meals and how they drive certain recipes". I think such a user is much more likely to be engaged in exploration if the meal titles are shown. It is still worthwhile keeping the dates—if nothing else, they provide a clue to the user as to how the between meal links work (i.e. they are date ordered).

Phew! Confident that I am justified in adding this next bit of behavior, I will need to modify the example of the current behavior:
describe "link_to_adjacent_view_date" do
context "couchdb view by_month" do
before(:each) do
@count_by_month = [{"key" => "2009-04", "value" => 3},
{"key" => "2009-05", "value" => 3}]
end

it "should link to block text + date, if block is given" do
link_to_adjacent_view_date("2009-04", @count_by_month) do |date|
"foo #{date} bar"
end.
should have_selector("a",
:href => "/meals/2009/05",
:content => "foo 2009-05 bar")
end
end
end
That example reads, given the CouchDB view (@count_by_month), linking to a date adjacent to the supplied date, I should be able to incorporate the date into my own date string (here, surrounding the date with the words "foo" and "bar").

Now I need to support two arguments, instead of one, so the example is updated to read:
    it "should link to the CouchDB view's key and value, if block is given" do
link_to_adjacent_view_date("2009-04", @count_by_month) do |date, value|
"foo #{date} bar #{value} baz"
end.
should have_selector("a",
:href => "/meals/2009/05",
:content => "foo 2009-05 bar 3 baz")
end
Now, I expect the link_to_adjacent_view_date helper to yield with the CouchDB value, in addition to the date (key) that it had been yielding. That example verifies that the value ("3" from the context above) is usable as I expect it to be.

Running the spec fails now:
cstrom@jaynestown:~/repos/eee-code$ spec ./spec/eee_helpers_spec.rb 
...............................F..............

1)
'link_to_adjacent_view_date couchdb view by_month should link to the CouchDB view's key and value, if block is given' FAILED
expected following output to contain a <a href='/meals/2009/05'>foo 2009-05 bar 3 baz</a> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><a href="/meals/2009/05">foo 2009-05 bar baz</a></body></html>
./spec/eee_helpers_spec.rb:241:

Finished in 0.044204 seconds

46 examples, 1 failure
I can make that pass by changing link_to_adjacent_view_date to yield the key and value to build the link text:
        link_text = block_given? ?
yield(next_result['key'], next_result['value']) :
next_result['key']
I have to update all instances of link_to_adjacent_view_date that use a block to expect two parameters (even if the second one is not used). With all specs passing, it is back to the meal spec to use the meal title in the link text:
  it "should link to previous meal title" do
render("/views/meal.haml")
response.should have_selector("a",
:href => "/meals/2009/05/15",
:content => "Foo")
end
(where "Foo" is the title a mock CouchDB view, defined in the spec setup)

To make that example pass, I change this line in the Haml template:
=link_to_adjacent_view_date(@meal['date'], @meals_by_date, :previous => true) { |d,v| Date.parse(d).strftime("%B %e, %Y") }
to read:
  =link_to_adjacent_view_date(@meal['date'], @meals_by_date, :previous => true) { |d,v| Date.parse(d).strftime("#{v[1]} (%B %e, %Y)") }
OK, now I am ready to work my way back out to the navigation-between-meals Cucumber scenario:



Yay! All passing. I am now assured that quick navigation between meals will work.
(commit)

I currently reside at the following Cucumber milemarker:
26 scenarios
1 skipped step
42 undefined steps
166 passed steps
Tomorrow, I will pick up with another scenario and try to get the number of undefined steps down under 40. I'm getting close.

Tuesday, June 9, 2009

Between Meals (Inside)

‹prev | My Chain | next›

Gonna be a bit brief today as I was presenting at the B'More on Rails users' group tonight. Still, I must make my daily offering to the chain gods, lest I fall into disfavor with them.

I left off last night with a failing Cucumber scenario:
cstrom@jaynestown:~/repos/eee-code$ cucumber features -n -s "Navigating between meals"
Sinatra::Test is deprecated; use Rack::Test instead.
Feature: Browse Meals

So that I can find meals made on special occasions
As a person interested in finding meals
I want to browse meals by date

Scenario: Navigating between meals
Given a "Pumpkin is a Very Exciting Vegetable" meal enjoyed on December 3, 2008
And a "Star Wars: The Dinner" meal enjoyed on February 28, 2009
And a "Focaccia! The Dinner" meal enjoyed on March 3, 2009
When I view the "Focaccia! The Dinner" meal
Then I should see the "Focaccia! The Dinner" title
When I click "Star Wars: The Dinner"
Could not find link with text or title or id "Star Wars: The Dinner" (Webrat::NotFoundError)
features/browse_meals.feature:61:in `When I click "Star Wars: The Dinner"'

Then I should see the "Star Wars: The Dinner" title
When I click "Pumpkin is a Very Exciting Vegetable"
Then I should see the "Pumpkin is a Very Exciting Vegetable" title
When I click "Star Wars: The Dinner"
Then I should see the "Star Wars: The Dinner" title
When I click "Focaccia! The Dinner"
Then I should see the "Focaccia! The Dinner" title

1 scenario
1 failed step
7 skipped steps
5 passed steps
For this, I will need a another CouchDB view that returns a date ordered list of meals:
{
"views": {
...
"by_date": {
"map": "function (doc) {
if (doc['type'] == 'Meal') {
emit(doc['date'], [doc['_id'], doc['title']]);
}
}"
},
...
}
}
I still have yet to find a way to drive the development of CouchDB Javascript views by example, so I will rely on the Cucumber scenario to verify that view. For now, I will use RSpec to describe how I want to use it in the meal Haml spec:
describe "meal.haml" do
before(:each) do
@title = "Meal Title"
@summary = "Meal Summary"
@description = "Meal Description"
@year = 2009
@month = 5
assigns[:meal] = @meal = {
'date' => "%d-%02d-31" % [@year, @month],
'title' => @title,
'summary' => @summary,
'description' => @description,
'menu' => ["Peanut Butter and Jelly Sandwich"]
}
assigns[:meals_by_date] = [{"key" => "2009-05-15", "value" => []},
{"key" => "2009-05-31", "value" => []}]

end
...
it "should link to previous meal" do
render("/views/meal.haml")
response.should have_selector("a", :href => "/meals/2009/05/15")
end

...
end
I already have a helper that pulls adjacent dates out of CouchDB views, I re-use this to implement that example:
...
%h1
= @meal['title']

%div.navigation
=link_to_adjacent_view_date(@meal['date'], @meals_by_date, :previous => true)
|
=link_to_adjacent_view_date(@meal['date'], @meals_by_date)
...
A CouchDB view and a simple implementation are enough for me to satisfy the gods tonight. I will pick back up tomorrow by linking humanized dates (it is now linking text like "2009-06-09") and then working my way back out to the Cucumber scenario to see if I am done with this scenario.
(commit)

Monday, June 8, 2009

Between Meals (Outside)

‹prev | My Chain | next›

With some Rack investigation under my belt, it is back to the drive towards deployment. That means back into Cucumber scenarios. Next up is the "navigation between meals" scenarios:



Aw, nuts:
Sinatra::Test is deprecated; use Rack::Test instead.
I upgraded everything from Sinatra::Test to Rack::Test last night. I even ack'd (thppt) through the code to make sure that I removed all references to the old Sinatra::Test. What gives?

Unfortunately, I need to punt on that question for tonight. I gave it a go following these instructions, but ended up with last_response vs. response issues. Will revisit later.

For now, I continue with the navigation-between-meals scenario. The next "undefined" step is already defined—I simply missed the word "a" in the text. Adding it, I get this far:
cstrom@jaynestown:~/repos/eee-code$ cucumber features -s "Navigating between meals"
Sinatra::Test is deprecated; use Rack::Test instead.
Feature: Browse Meals

So that I can find meals made on special occasions
As a person interested in finding meals
I want to browse meals by date

Scenario: Navigating between meals # features/browse_meals.feature:54
Given a "Focaccia! The Dinner" meal enjoyed on March 3, 2009 # features/step_definitions/meal_details.rb:1
And a "Star Wars: The Dinner" meal enjoyed on February 28, 2009 # features/step_definitions/meal_details.rb:1
And a "Pumpkin is a Very Exciting Vegetable" meal enjoyed on December 3, 2008 # features/step_definitions/meal_details.rb:1
When I view the "Focaccia! The Dinner" meal # features/step_definitions/meal_details.rb:52
Then I should see the "Focaccia! The Dinner" title # features/step_definitions/meal_details.rb:65
expected following output to contain a <h1>Focaccia! The Dinner</h1> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<div class="breadcrumbs">
<a href="/">home</a> > <a href="/meals/2008">2008</a> > <a href="/meals/2008/12">December</a> > <span>3</span>
</div>
<h1>
Pumpkin is a Very Exciting Vegetable
</h1>
<div id="summary">
<p>meal summary</p>
</div>
<ul id="menu"></ul>
<div id="description">
<p>meal description</p>
</div>
</body></html>
(Spec::Expectations::ExpectationNotMetError)
features/browse_meals.feature:60:in `Then I should see the "Focaccia! The Dinner" title'
The reason the pumpkin meal is displaying instead of the focaccia meal is that the pumpkin meal is the last of the given steps. As the last of the given steps, it defines the instance variables used to drive the when-i-view step.

I ought to be able to resolve this, without compromising the integrity of the scenario, by re-arranging the given steps:



Yup—passing all the way up to an undefined step, which I define as:
When /^I click "([^\"]*)"$/ do |text|
click_link text
end
With that, I have all steps defined, but the navigation link steps not passing:
cstrom@jaynestown:~/repos/eee-code$ cucumber features -n -s "Navigating between meals"
Sinatra::Test is deprecated; use Rack::Test instead.
Feature: Browse Meals

So that I can find meals made on special occasions
As a person interested in finding meals
I want to browse meals by date

Scenario: Navigating between meals
Given a "Pumpkin is a Very Exciting Vegetable" meal enjoyed on December 3, 2008
And a "Star Wars: The Dinner" meal enjoyed on February 28, 2009
And a "Focaccia! The Dinner" meal enjoyed on March 3, 2009
When I view the "Focaccia! The Dinner" meal
Then I should see the "Focaccia! The Dinner" title
When I click "Star Wars: The Dinner"
Could not find link with text or title or id "Star Wars: The Dinner" (Webrat::NotFoundError)
features/browse_meals.feature:61:in `When I click "Star Wars: The Dinner"'
Then I should see the "Star Wars: The Dinner" title
When I click "Pumpkin is a Very Exciting Vegetable"
Then I should see the "Pumpkin is a Very Exciting Vegetable" title
When I click "Star Wars: The Dinner"
Then I should see the "Star Wars: The Dinner" title
When I click "Focaccia! The Dinner"
Then I should see the "Focaccia! The Dinner" title

1 scenario
1 failed step
7 skipped steps
5 passed steps
I think I will stop there for the day. I still have much work to do on my presentation for the B'More on Rails group. Besides, it will be easy to know where to pick up tomorrow—working my way into the code to implement these steps.