With an understanding between me and couchdb-lucene sorting, I start back with implementation. In the Sinatra application's spec for
/recipes/search
, I add:it "should sort" doI make this example pass by simply passing the sort parameter through to couchdb-lucene:
RestClient.should_receive(:get).
with(/sort=title/).
and_return('{"total_rows":30,"skip":0,"limit":20,"rows":[]}')
get "/recipes/search?q=title:egg&sort=title"
end
data = RestClient.get "#{@@db}/_fti?limit=20&skip=#{skip}&q=#{params[:q]}&sort=#{params[:sort]}"That spec may pass, but my cucumber scenario no longer does:
cstrom@jaynestown:~/repos/eee-code$ cucumber -n features \The cucumber scenario is not even reaching the sorting steps—it is failing on the simple search-for-a-string step. The cause of the failure is couchdb-lucene's dislike of empty (or non-indexed) sort fields. I have to guard against empty sort parameters:
-s "Sorting (name, date, preparation time, number of ingredients)"
Feature: Search for recipes
So that I can find one recipe among many
As a web user
I want to be able search recipes
Scenario: Sorting (name, date, preparation time, number of ingredients)
Given 50 "delicious" recipes with ascending names, dates, preparation times, and number of ingredients
And a 0.5 second wait to allow the search index to be updated
When I search for "delicious"
HTTP status code 400 (RestClient::RequestFailed)
/usr/lib/ruby/1.8/net/http.rb:543:in `start'
./features/support/../../eee.rb:30:in `GET /recipes/search'
(eval):7:in `get'
features/recipe_search.feature:79:in `When I search for "delicious"'
Then I should see 20 results
When I click the "Name" column header
...
it "should not sort when no sort field is supplied" doI can implement this example thusly:
RestClient.stub!(:get).
and_return('{"total_rows":30,"skip":0,"limit":20,"rows":[]}')
RestClient.should_not_receive(:get).with(/sort=/)
get "/recipes/search?q=title:egg&sort="
end
get '/recipes/search' doWith that, my Cucumber scenarios are again passing and I am ready to proceed with the view / helper work.
@query = params[:q]
page = params[:page].to_i
skip = (page < 2) ? 0 : ((page - 1) * 20) + 1
couchdb_url = "#{@@db}/_fti?limit=20" +
"&q=#{@query}" +
"&skip=#{skip}"
if params[:sort] =~ /\w/
couchdb_url += "&sort=#{params[:sort]}"
end
data = RestClient.get couchdb_url
@results = JSON.parse(data)
if @results['rows'].size == 0 && page > 1
redirect("/recipes/search?q=#{@query}")
return
end
haml :search
end
(commit)
Shortly after starting work in the Haml template, the sort field gets unwieldy, which is a good indication that it ought to be a helper. I opt for the name of
sort_link
for the helper and build the following examples to describe how it should work:describe "sort_link" doI implement this code as:
it "should link the supplied text" do
sort_link("Foo", "sort_foo", "query").
should have_selector("a",
:content => "Foo")
end
it "should link to the query with the supplied sort field" do
sort_link("Foo", "sort_foo", "query").
should have_selector("a",
:href => "/recipes/search?q=query&sort=sort_foo")
end
end
def sort_link(text, sort_on, query)There are no example for the link's
id = "sort-by-#{text.downcase}"
url = "/recipes/search?q=#{query}&sort=#{sort_on}"
%Q|#{text}|
end
id
. That is semantic information, having nothing to do with behavior of the application. The only reason to include it is for styling and, more importantly, the Cucumber scenario.Speaking of the Cucumber scenario, I am now ready to implement the next step,
Then the results should be ordered by name in ascending order
, which is aided by some CSS selector fanciness:Then /^the results should be ordered by name in ascending order$/ doThe first child of the results table is the header, which is the reason the first selector is looking for the second child. The reason for the second test is that I want to ensure that sorting has taken place. The "delicious recipe 1" was the first recipe entered, so it may show up in the results list first for that reason alone. But "delicious recipe 10" will come before "delicious recipe 2" only if they have been sorted (because the "1" in "10" comes before "2" when performing text sorting).
response.should have_selector("tr:nth-child(2) a",
:content => "delicious recipe 1")
response.should have_selector("tr:nth-child(3) a",
:content => "delicious recipe 10")
end
(commit)
Up next: reversing the sort order.